From b50495bfa5f2c40fa1bb1498c0e468d7d9ec67c3 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 5 Jul 2022 12:26:30 +0200 Subject: [PATCH] WIP: Support clone in userspace Everything seems to work for the most part, but now there are tons of daemons which rely on syscall::clone, which is now implemented in relibc :( --- src/context/arch/x86_64.rs | 2 +- src/context/context.rs | 18 ++- src/context/memory.rs | 10 +- src/ptrace.rs | 6 +- src/scheme/mod.rs | 11 +- src/scheme/proc.rs | 283 ++++++++++++++++++++++++++----------- 6 files changed, 237 insertions(+), 93 deletions(-) diff --git a/src/context/arch/x86_64.rs b/src/context/arch/x86_64.rs index c367729..09d0a79 100644 --- a/src/context/arch/x86_64.rs +++ b/src/context/arch/x86_64.rs @@ -35,7 +35,7 @@ pub struct Context { /// Base pointer rbp: usize, /// Stack pointer - rsp: usize, + pub(crate) rsp: usize, /// FSBASE. /// /// NOTE: Same fsgsbase behavior as with gsbase. diff --git a/src/context/context.rs b/src/context/context.rs index b351ee5..34e11b8 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -12,7 +12,7 @@ use core::{ }; use spin::RwLock; -use crate::arch::{interrupt::InterruptStack, paging::PAGE_SIZE}; +use crate::arch::{interrupt::InterruptStack, paging::{PAGE_SIZE, RmmA, RmmArch}}; use crate::common::unique::Unique; use crate::context::arch; use crate::context::file::{FileDescriptor, FileDescription}; @@ -250,6 +250,11 @@ pub struct Context { /// else than SIG_DFL, otherwise signals will not be delivered. Userspace is responsible for /// setting this. pub sigstack: Option, + /// An even hackier way to pass the return entry point and stack pointer to new contexts while + /// implementing clone. Before a context has returned to userspace, its IntRegisters cannot be + /// set since there is no interrupt stack (unless the kernel stack is copied, but that is in my + /// opinion hackier and less efficient than this (and UB to do in Rust)). + pub clone_entry: Option<[usize; 2]>, } // Necessary because GlobalAlloc::dealloc requires the layout to be the same, and therefore Box @@ -352,6 +357,7 @@ impl Context { regs: None, ptrace_stop: false, sigstack: None, + clone_entry: None, }; this.set_addr_space(new_addrspace()?.1); Ok(this) @@ -528,8 +534,14 @@ impl Context { self.addr_space.as_ref().ok_or(Error::new(ESRCH)) } pub fn set_addr_space(&mut self, addr_space: Arc>) { - assert!(!self.running); - self.arch.set_page_utable(addr_space.read().frame.utable.start_address().data()); + let physaddr = addr_space.read().frame.utable.start_address(); + if self.running { + unsafe { + RmmA::set_table(physaddr); + } + } + + self.arch.set_page_utable(physaddr.data()); self.addr_space = Some(addr_space); } } diff --git a/src/context/memory.rs b/src/context/memory.rs index 95e57f5..1c72958 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -77,8 +77,7 @@ impl 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 { + let mut this_mapper = if self.is_current() { active = unsafe { ActivePageTable::new(rmm::TableKind::User) }; active.mapper() } else { @@ -95,8 +94,8 @@ impl AddrSpace { 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; + let current_frame = unsafe { RmmA::phys_to_virt(this_mapper.translate_page(page).ok_or(Error::new(ENOMEM))?.start_address()) }.data() as *const u8; + let new_frame = unsafe { RmmA::phys_to_virt(new_mapper.mapper().translate_page(page).ok_or(Error::new(ENOMEM))?.start_address()) }.data() as *mut u8; // TODO: Replace this with CoW unsafe { @@ -115,6 +114,9 @@ impl AddrSpace { id, }) } + pub fn is_current(&self) -> bool { + self.frame.utable.start_address() == unsafe { RmmA::table() } + } } #[derive(Debug)] diff --git a/src/ptrace.rs b/src/ptrace.rs index dc76ee4..302646e 100644 --- a/src/ptrace.rs +++ b/src/ptrace.rs @@ -13,7 +13,7 @@ use crate::{ } }, common::unique::Unique, - context::{self, signal, Context, ContextId}, + context::{self, signal, Context, ContextId, memory::AddrSpace}, event, scheme::proc, sync::WaitCondition, @@ -472,8 +472,8 @@ fn page_aligned_chunks(mut start: usize, mut len: usize) -> impl Iterator impl Iterator> + '_ { - let mut table = unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) }; +pub fn context_memory(addrspace: &mut AddrSpace, offset: VirtualAddress, len: usize) -> impl Iterator> + '_ { + let mut table = unsafe { InactivePageTable::from_address(addrspace.frame.utable.start_address().data()) }; // TODO: Iterate over grants instead to avoid yielding None too many times. What if // context_memory is used for an entire process's address space, where the stack is at the very diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index 0311356..4e89661 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -16,7 +16,7 @@ use alloc::{ use core::sync::atomic::AtomicUsize; use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; -use crate::context::Context; +use crate::context::{Context, memory::AddrSpace, file::FileDescriptor}; use crate::syscall::error::*; use crate::syscall::scheme::Scheme; @@ -300,10 +300,17 @@ pub fn schemes_mut() -> RwLockWriteGuard<'static, SchemeList> { SCHEMES.call_once(init_schemes).write() } +#[allow(unused_variables)] pub trait KernelScheme: Scheme + Send + Sync + 'static { - #[allow(unused_variables)] fn kfmap(&self, number: usize, map: &syscall::data::Map, target_context: &Arc>) -> Result { log::error!("Returning ENOSYS since kfmap can only be called on UserScheme schemes"); Err(Error::new(ENOSYS)) } + + fn as_filetable(&self, number: usize) -> Result>>>> { + Err(Error::new(EBADF)) + } + fn as_addrspace(&self, number: usize) -> Result>> { + Err(Error::new(EBADF)) + } } diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 31222a3..2d0c3cc 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -1,9 +1,9 @@ use crate::{ 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}}, + context::{self, Context, ContextId, Status, file::FileDescriptor, memory::{AddrSpace, Grant, new_addrspace, PtId, page_flags, Region}}, memory::PAGE_SIZE, ptrace, - scheme::{AtomicSchemeId, SchemeId}, + scheme::{self, AtomicSchemeId, FileHandle, KernelScheme, SchemeId}, syscall::{ FloatRegisters, IntRegisters, @@ -107,19 +107,22 @@ enum RegsKind { Int, Env, } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone)] enum Operation { - Memory, - Grants, + Memory { addrspace: Arc> }, Regs(RegsKind), Trace, Static(&'static str), Name, + Cwd, Sigstack, Attr(Attr), - Files, - AddrSpace { id: PtId }, + Filetable { filetable: Arc>>> }, + AddrSpace { addrspace: Arc> }, CurrentAddrSpace, + CurrentFiletable, + // TODO: Any better interface to access newly created contexts? Openat? + OpenViaDup, } #[derive(Clone, Copy, PartialEq, Eq)] enum Attr { @@ -128,10 +131,10 @@ enum Attr { // TODO: namespace, tid, etc. } impl Operation { - fn needs_child_process(self) -> bool { - matches!(self, Self::Memory | Self::Grants | Self::Regs(_) | Self::Trace | Self::Files) + fn needs_child_process(&self) -> bool { + matches!(self, Self::Memory { .. } | Self::Regs(_) | Self::Trace | Self::Filetable { .. } | Self::AddrSpace { .. } | Self::CurrentAddrSpace | Self::CurrentFiletable) } - fn needs_root(self) -> bool { + fn needs_root(&self) -> bool { matches!(self, Self::Attr(_)) } } @@ -186,7 +189,7 @@ impl OperationData { } } -#[derive(Clone, Copy)] +#[derive(Clone)] struct Info { pid: ContextId, flags: usize, @@ -255,34 +258,29 @@ impl ProcScheme { } } -impl Scheme for ProcScheme { - fn open(&self, path: &str, flags: usize, uid: u32, gid: u32) -> Result { - let mut parts = path.splitn(2, '/'); - let pid_str = parts.next() - .ok_or(Error::new(ENOENT))?; +fn current_addrspace() -> Result>> { + Ok(Arc::clone(context::contexts().current().ok_or(Error::new(ESRCH))?.read().addr_space()?)) +} - let pid = if pid_str == "current" { - context::context_id() - } else if self.access == Access::Restricted { - return Err(Error::new(EACCES)); - } else { - ContextId::from(pid_str.parse().map_err(|_| Error::new(ENOENT))?) - }; - - let operation = match parts.next() { - Some("mem") => Operation::Memory, - Some("addrspace") => Operation::AddrSpace { id: context::contexts().current().ok_or(Error::new(ESRCH))?.read().addr_space()?.read().id }, +impl ProcScheme { + fn open_inner(&self, pid: ContextId, operation_str: Option<&str>, flags: usize, uid: u32, gid: u32) -> Result { + let operation = match operation_str { + Some("mem") => Operation::Memory { addrspace: current_addrspace()? }, + Some("addrspace") => Operation::AddrSpace { addrspace: current_addrspace()? }, + Some("filetable") => Operation::Filetable { filetable: Arc::clone(&context::contexts().current().ok_or(Error::new(ESRCH))?.read().files) }, Some("current-addrspace") => Operation::CurrentAddrSpace, + Some("current-filetable") => Operation::CurrentFiletable, Some("regs/float") => Operation::Regs(RegsKind::Float), Some("regs/int") => Operation::Regs(RegsKind::Int), Some("regs/env") => Operation::Regs(RegsKind::Env), Some("trace") => Operation::Trace, Some("exe") => Operation::Static("exe"), Some("name") => Operation::Name, + Some("cwd") => Operation::Cwd, Some("sigstack") => Operation::Sigstack, Some("uid") => Operation::Attr(Attr::Uid), Some("gid") => Operation::Attr(Attr::Gid), - Some("files") => Operation::Files, + Some("open_via_dup") => Operation::OpenViaDup, _ => return Err(Error::new(EINVAL)) }; @@ -295,7 +293,7 @@ impl Scheme for ProcScheme { let target = target.read(); data = match operation { - Operation::Memory => OperationData::Memory(MemData::default()), + Operation::Memory { .. } => OperationData::Memory(MemData::default()), Operation::Trace => OperationData::Trace(TraceData::default()), Operation::Static(_) => OperationData::Static(StaticData::new( target.name.read().clone().into() @@ -335,7 +333,7 @@ impl Scheme for ProcScheme { return Err(Error::new(EPERM)); } - if matches!(operation, Operation::Files) { + if matches!(operation, Operation::Filetable { .. }) { data = OperationData::Static(StaticData::new({ use core::fmt::Write; @@ -352,7 +350,7 @@ impl Scheme for ProcScheme { info: Info { flags, pid, - operation, + operation: operation.clone(), }, data, })?; @@ -372,6 +370,26 @@ impl Scheme for ProcScheme { Ok(id) } +} + +impl Scheme for ProcScheme { + fn open(&self, path: &str, flags: usize, uid: u32, gid: u32) -> Result { + let mut parts = path.splitn(2, '/'); + let pid_str = parts.next() + .ok_or(Error::new(ENOENT))?; + + let pid = if pid_str == "current" { + context::context_id() + } else if pid_str == "new" { + inherit_context()? + } else if self.access == Access::Restricted { + return Err(Error::new(EACCES)); + } else { + ContextId::from(pid_str.parse().map_err(|_| Error::new(ENOENT))?) + }; + + self.open_inner(pid, parts.next(), flags, uid, gid) + } /// Dup is currently used to implement clone() and execve(). fn dup(&self, old_id: usize, buf: &[u8]) -> Result { @@ -379,18 +397,41 @@ impl Scheme for ProcScheme { let handles = self.handles.read(); let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?; - handle.info + handle.info.clone() }; self.new_handle(match info.operation { - Operation::AddrSpace { id } => { - let new_ptid = match buf { + Operation::OpenViaDup => { + let (uid, gid) = match &*context::contexts().current().ok_or(Error::new(ESRCH))?.read() { + context => (context.euid, context.egid), + }; + return self.open_inner(info.pid, Some(core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?).filter(|s| !s.is_empty()), O_RDWR | O_CLOEXEC, uid, gid); + }, + + Operation::Filetable { filetable } => { + // TODO: Maybe allow userspace to either copy or transfer recently dupped file + // descriptors between file tables. + if buf != b"copy" { + return Err(Error::new(EINVAL)); + } + let new_filetable = Arc::try_new(RwLock::new(filetable.read().iter().cloned().collect::>())).map_err(|_| Error::new(ENOMEM))?; + + Handle { + info: Info { + flags: 0, + pid: info.pid, + operation: Operation::Filetable { filetable: new_filetable }, + }, + data: OperationData::Other, + } + } + Operation::AddrSpace { addrspace } => { + let (new_addrspace, is_mem) = 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, + b"empty" => (new_addrspace()?.1, false), + b"exclusive" => (addrspace.read().try_clone()?.1, false), + b"mem" => (Arc::clone(&addrspace), true), _ => return Err(Error::new(EINVAL)), }; @@ -398,9 +439,9 @@ impl Scheme for ProcScheme { info: Info { flags: 0, pid: info.pid, - operation: Operation::AddrSpace { id: new_ptid }, + operation: if is_mem { Operation::Memory { addrspace: new_addrspace } } else { Operation::AddrSpace { addrspace: new_addrspace } }, }, - data: OperationData::Other, + data: if is_mem { OperationData::Memory(MemData { offset: VirtualAddress::new(0) }) } else { OperationData::Other }, } } _ => return Err(Error::new(EINVAL)), @@ -429,11 +470,10 @@ impl Scheme for ProcScheme { let info = { let handles = self.handles.read(); let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - handle.info + handle.info.clone() }; 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))?; @@ -444,19 +484,15 @@ impl Scheme for ProcScheme { data.offset += len; Ok(len) }, - Operation::Memory => { + Operation::Memory { addrspace } => { // Won't context switch, don't worry about the locks let mut handles = self.handles.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; let data = handle.data.mem_data().expect("operations can't change"); - let contexts = context::contexts(); - let context = contexts.get(info.pid).ok_or(Error::new(ESRCH))?; - let mut context = context.write(); - let mut bytes_read = 0; - for chunk_opt in ptrace::context_memory(&mut *context, data.offset, buf.len()) { + for chunk_opt in ptrace::context_memory(&mut *addrspace.write(), data.offset, buf.len()) { let chunk = chunk_opt.ok_or(Error::new(EFAULT))?; let dst_slice = &mut buf[bytes_read..bytes_read + chunk.len()]; unsafe { @@ -468,6 +504,7 @@ impl Scheme for ProcScheme { data.offset = VirtualAddress::new(data.offset.data() + bytes_read); Ok(bytes_read) }, + // TODO: Support querying which grants exist and where Operation::AddrSpace { .. } => return Err(Error::new(EBADF)), Operation::Regs(kind) => { @@ -582,6 +619,7 @@ impl Scheme for ProcScheme { Ok(read * mem::size_of::()) } Operation::Name => read_from(buf, context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().name.read().as_bytes(), &mut 0), + Operation::Cwd => read_from(buf, context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().cwd.read().as_bytes(), &mut 0), Operation::Sigstack => read_from(buf, &context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().sigstack.unwrap_or(!0).to_ne_bytes(), &mut 0), Operation::Attr(attr) => { let src_buf = match (attr, &*Arc::clone(context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?).read()) { @@ -591,7 +629,7 @@ impl Scheme for ProcScheme { read_from(buf, &src_buf, &mut 0) } - Operation::Files => { + Operation::Filetable { .. } => { let mut handles = self.handles.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; let data = handle.data.static_data().expect("operations can't change"); @@ -602,10 +640,8 @@ impl Scheme for ProcScheme { // TODO: Find a better way to switch address spaces, since they also require switching // the instruction and stack pointer. Maybe remove `/regs` altogether and replace it // with `/ctx` - Operation::CurrentAddrSpace => { - //read_from(buf, &usize::to_ne_bytes(id.into()), &mut 0) - Ok(0) - } + Operation::CurrentAddrSpace | Operation::CurrentFiletable => return Err(Error::new(EBADF)), + Operation::OpenViaDup => return Err(Error::new(EBADF)), } } @@ -622,25 +658,20 @@ impl Scheme for ProcScheme { let mut handles = self.handles.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; handle.continue_ignored_children(); - handle.info + handle.info.clone() }; match info.operation { - Operation::Grants => Err(Error::new(ENOSYS)), Operation::Static(_) => Err(Error::new(EBADF)), - Operation::Memory => { + Operation::Memory { addrspace } => { // Won't context switch, don't worry about the locks let mut handles = self.handles.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; let data = handle.data.mem_data().expect("operations can't change"); - let contexts = context::contexts(); - let context = contexts.get(info.pid).ok_or(Error::new(ESRCH))?; - let mut context = context.write(); - let mut bytes_written = 0; - for chunk_opt in ptrace::context_memory(&mut *context, data.offset, buf.len()) { + for chunk_opt in ptrace::context_memory(&mut *addrspace.write(), data.offset, buf.len()) { let chunk = chunk_opt.ok_or(Error::new(EFAULT))?; let src_slice = &buf[bytes_written..bytes_written + chunk.len()]; unsafe { @@ -652,7 +683,7 @@ impl Scheme for ProcScheme { data.offset = VirtualAddress::new(data.offset.data() + bytes_written); Ok(bytes_written) }, - Operation::AddrSpace { .. } => { + Operation::AddrSpace { addrspace } => { // FIXME: Forbid upgrading external mappings. let pid = self.handles.read() @@ -670,18 +701,19 @@ impl Scheme for ProcScheme { return Err(Error::new(EINVAL)); } - let is_active = pid == context::context_id(); + let mut addrspace = addrspace.write(); + let is_active = addrspace.is_current(); - let callback = |context: &mut Context| { + let callback = |addr_space: &mut AddrSpace| { let (mut inactive, mut active); - let mut addr_space = context.addr_space()?.write(); + //let mut addr_space = context.addr_space()?.write(); 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) } else { - inactive = (unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) }, InactiveFlusher::new()); + inactive = (unsafe { InactivePageTable::from_address(addr_space.frame.utable.start_address().data()) }, InactiveFlusher::new()); (inactive.0.mapper(), &mut inactive.1 as &mut dyn Flusher) }; @@ -714,12 +746,17 @@ impl Scheme for ProcScheme { } Ok(()) }; + callback(&mut *addrspace)?; - if is_active { + // TODO: Set some "in use" flag every time an address space is switched to. This + // way, we know what hardware threads are using any given page table, which we need + // to know while doing TLB shootdown. + + /*if is_active { with_context_mut(pid, callback)?; } else { try_stop_context(pid, callback)?; - } + }*/ Ok(3 * mem::size_of::()) } Operation::Regs(kind) => match kind { @@ -868,11 +905,17 @@ impl Scheme for ProcScheme { Ok(mem::size_of::()) }, + // TODO: Deduplicate name and cwd Operation::Name => { let utf8 = alloc::string::String::from_utf8(buf.to_vec()).map_err(|_| Error::new(EINVAL))?.into_boxed_str(); *context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().name.write() = utf8; Ok(buf.len()) } + Operation::Cwd => { + let utf8 = alloc::string::String::from_utf8(buf.to_vec()).map_err(|_| Error::new(EINVAL))?; + *context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().cwd.write() = utf8; + Ok(buf.len()) + } Operation::Sigstack => { let bytes = <[u8; mem::size_of::()]>::try_from(buf).map_err(|_| Error::new(EINVAL))?; let sigstack = usize::from_ne_bytes(bytes); @@ -889,25 +932,49 @@ impl Scheme for ProcScheme { } Ok(buf.len()) } - Operation::Files => return Err(Error::new(EBADF)), + Operation::Filetable { .. } => return Err(Error::new(EBADF)), + Operation::CurrentFiletable => { + let filetable_fd = usize::from_ne_bytes(<[u8; mem::size_of::()]>::try_from(buf).map_err(|_| Error::new(EINVAL))?); + let (hopefully_this_scheme, number) = extract_scheme_number(filetable_fd)?; + + let mut filetable = hopefully_this_scheme.as_filetable(number)?; + + try_stop_context(info.pid, |context| { + context.files = filetable; + Ok(()) + })?; + Ok(mem::size_of::()) + } Operation::CurrentAddrSpace { .. } => { + println!("Setting current address space! ({} {})", info.pid.into(), context::context_id().into()); + let mut iter = buf.array_chunks::<{mem::size_of::()}>().copied().map(usize::from_ne_bytes); - let id = iter.next().ok_or(Error::new(EINVAL))?; + let addrspace_fd = 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))?; + let (hopefully_this_scheme, number) = extract_scheme_number(addrspace_fd)?; + let space = hopefully_this_scheme.as_addrspace(number)?; - 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; + let callback = |context: &mut Context| unsafe { + if let Some(saved_regs) = ptrace::regs_for_mut(context) { + saved_regs.iret.rip = ip; + saved_regs.iret.rsp = sp; + } else { + context.clone_entry = Some([ip, sp]); + } context.set_addr_space(space); Ok(()) - })?; + }; + if info.pid == context::context_id() { + with_context_mut(info.pid, callback)?; + } else { + try_stop_context(info.pid, callback)?; + } Ok(3 * mem::size_of::()) } + Operation::OpenViaDup => return Err(Error::new(EBADF)), } } @@ -939,20 +1006,22 @@ impl Scheme for ProcScheme { let handle = handles.get(&id).ok_or(Error::new(EBADF))?; let path = format!("proc:{}/{}", handle.info.pid.into(), match handle.info.operation { - Operation::Memory => "mem", - Operation::Grants => "grants", + Operation::Memory { .. } => "mem", Operation::Regs(RegsKind::Float) => "regs/float", Operation::Regs(RegsKind::Int) => "regs/int", Operation::Regs(RegsKind::Env) => "regs/env", Operation::Trace => "trace", Operation::Static(path) => path, Operation::Name => "name", + Operation::Cwd => "cwd", Operation::Sigstack => "sigstack", Operation::Attr(Attr::Uid) => "uid", Operation::Attr(Attr::Gid) => "gid", - Operation::Files => "files", + Operation::Filetable { .. } => "filetable", Operation::AddrSpace { .. } => "addrspace", Operation::CurrentAddrSpace => "current-addrspace", + Operation::CurrentFiletable => "current-filetable", + Operation::OpenViaDup => "open-via-dup", }); read_from(buf, &path.as_bytes(), &mut 0) @@ -999,4 +1068,58 @@ impl Scheme for ProcScheme { Ok(0) } } -impl crate::scheme::KernelScheme for ProcScheme {} +impl KernelScheme for ProcScheme { + fn as_addrspace(&self, number: usize) -> Result>> { + if let Operation::AddrSpace { ref addrspace } | Operation::Memory { ref addrspace } = self.handles.read().get(&number).ok_or(Error::new(EBADF))?.info.operation { + Ok(Arc::clone(addrspace)) + } else { + Err(Error::new(EBADF)) + } + } + fn as_filetable(&self, number: usize) -> Result>>>> { + if !matches!(self.handles.read().get(&number).ok_or(Error::new(EBADF))?.info.operation, Operation::Filetable { .. }) { + return Err(Error::new(EBADF)); + } + Ok(Arc::clone(&context::contexts().current().ok_or(Error::new(ESRCH))?.read().files)) + } +} +extern "C" fn clone_handler() { + let context_lock = Arc::clone(context::contexts().current().expect("expected the current context to be set in a spawn closure")); + + #[cfg(target_arch = "x86_64")] + unsafe { + let [ip, sp] = context_lock.read().clone_entry.expect("clone_entry must be set"); + let [arg, is_singlestep] = [0; 2]; + + crate::start::usermode(ip, sp, arg, is_singlestep); + } +} + +fn inherit_context() -> Result { + let current_context_lock = Arc::clone(context::contexts().current().ok_or(Error::new(ESRCH))?); + let new_context_lock = Arc::clone(context::contexts_mut().spawn(clone_handler)?); + + let current_context = current_context_lock.read(); + let mut new_context = new_context_lock.write(); + + new_context.status = Status::Stopped(SIGSTOP); + new_context.euid = current_context.euid; + new_context.egid = current_context.egid; + new_context.ruid = current_context.ruid; + new_context.rgid = current_context.rgid; + new_context.ens = current_context.ens; + new_context.rns = current_context.rns; + new_context.ppid = current_context.id; + + // TODO: More to copy? + + Ok(new_context.id) +} +fn extract_scheme_number(fd: usize) -> Result<(Arc, usize)> { + let (scheme_id, number) = match &*context::contexts().current().ok_or(Error::new(ESRCH))?.read().get_file(FileHandle::from(fd)).ok_or(Error::new(EBADF))?.description.read() { + desc => (desc.scheme, desc.number) + }; + let scheme = Arc::clone(scheme::schemes().get(scheme_id).ok_or(Error::new(ENODEV))?); + + Ok((scheme, number)) +}