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
:(
This commit is contained in:
4lDO2
2022-07-05 12:26:30 +02:00
parent 283ada82a0
commit b50495bfa5
6 changed files with 237 additions and 93 deletions

View File

@@ -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.

View File

@@ -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<usize>,
/// 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<RwLock<AddrSpace>>) {
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);
}
}

View File

@@ -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)]

View File

@@ -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<Item =
first.into_iter().chain((start..start + len).step_by(PAGE_SIZE).map(|off| (off, PAGE_SIZE))).chain(last)
}
pub fn context_memory(context: &mut Context, offset: VirtualAddress, len: usize) -> impl Iterator<Item = Option<*mut [u8]>> + '_ {
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<Item = Option<*mut [u8]>> + '_ {
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

View File

@@ -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<RwLock<Context>>) -> Result<usize> {
log::error!("Returning ENOSYS since kfmap can only be called on UserScheme schemes");
Err(Error::new(ENOSYS))
}
fn as_filetable(&self, number: usize) -> Result<Arc<RwLock<Vec<Option<FileDescriptor>>>>> {
Err(Error::new(EBADF))
}
fn as_addrspace(&self, number: usize) -> Result<Arc<RwLock<AddrSpace>>> {
Err(Error::new(EBADF))
}
}

View File

@@ -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<RwLock<AddrSpace>> },
Regs(RegsKind),
Trace,
Static(&'static str),
Name,
Cwd,
Sigstack,
Attr(Attr),
Files,
AddrSpace { id: PtId },
Filetable { filetable: Arc<RwLock<Vec<Option<FileDescriptor>>>> },
AddrSpace { addrspace: Arc<RwLock<AddrSpace>> },
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<usize> {
let mut parts = path.splitn(2, '/');
let pid_str = parts.next()
.ok_or(Error::new(ENOENT))?;
fn current_addrspace() -> Result<Arc<RwLock<AddrSpace>>> {
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<usize> {
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<usize> {
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<usize> {
@@ -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::<Vec<_>>())).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::<PtraceEvent>())
}
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 `<pid>/regs` altogether and replace it
// with `<pid>/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<RmmA>)
} 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<RmmA>)
};
@@ -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::<usize>())
}
Operation::Regs(kind) => match kind {
@@ -868,11 +905,17 @@ impl Scheme for ProcScheme {
Ok(mem::size_of::<u64>())
},
// 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::<usize>()]>::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::<usize>()]>::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::<usize>())
}
Operation::CurrentAddrSpace { .. } => {
println!("Setting current address space! ({} {})", info.pid.into(), context::context_id().into());
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 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::<usize>())
}
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<Arc<RwLock<AddrSpace>>> {
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<Arc<RwLock<Vec<Option<FileDescriptor>>>>> {
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<ContextId> {
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<dyn KernelScheme>, 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))
}