diff --git a/src/context/memory.rs b/src/context/memory.rs index fa553fc39d..1ff6df9fc8 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -1,5 +1,6 @@ use alloc::collections::BTreeMap; use alloc::{sync::Arc, vec::Vec}; +use syscall::GrantFlags; use core::cmp; use core::fmt::Debug; use core::num::NonZeroUsize; @@ -1063,6 +1064,45 @@ impl GrantInfo { _ => false, } } + pub fn grant_flags(&self) -> GrantFlags { + let mut flags = GrantFlags::empty(); + // TODO: has_read + flags.set(GrantFlags::GRANT_READ, true); + + flags.set(GrantFlags::GRANT_WRITE, self.flags.has_write()); + flags.set(GrantFlags::GRANT_EXEC, self.flags.has_execute()); + + // TODO: Set GRANT_LAZY + + match self.provider { + Provider::External { is_pinned_userscheme_borrow, .. } => { + flags.set(GrantFlags::GRANT_PINNED, is_pinned_userscheme_borrow); + flags |= GrantFlags::GRANT_SHARED; + } + Provider::Allocated { ref cow_file_ref } => { + // !GRANT_SHARED is equivalent to "GRANT_PRIVATE" + flags.set(GrantFlags::GRANT_SCHEME, cow_file_ref.is_some()); + } + Provider::PhysBorrowed { is_pinned_userscheme_borrow, .. } => { + flags |= GrantFlags::GRANT_SHARED | GrantFlags::GRANT_PHYS; + flags.set(GrantFlags::GRANT_PINNED, is_pinned_userscheme_borrow); + } + Provider::FmapBorrowed { .. } => { + flags |= GrantFlags::GRANT_SHARED | GrantFlags::GRANT_SCHEME; + } + } + + flags + } + pub fn file_ref(&self) -> Option<&GrantFileRef> { + // TODO: This would be bad for PhysBorrowed head/tail buffers, but otherwise the physical + // base address could be included in offset, for PhysBorrowed. + if let Provider::FmapBorrowed { ref file_ref } | Provider::Allocated { cow_file_ref: Some(ref file_ref) } = self.provider { + Some(file_ref) + } else { + None + } + } } impl Drop for GrantInfo { diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 34eb7e9b46..20fb0e6e50 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -8,10 +8,10 @@ use crate::{ FloatRegisters, IntRegisters, EnvRegisters, - data::{Map, PtraceEvent, SigAction, Stat}, + data::{GrantDesc, Map, PtraceEvent, SigAction, Stat}, error::*, flag::*, - scheme::{calc_seek_offset_usize, Scheme}, + scheme::{CallerCtx, calc_seek_offset_usize, Scheme}, self, usercopy::{UserSliceWo, UserSliceRo}, }, }; @@ -23,7 +23,6 @@ use alloc::{ sync::Arc, vec::Vec, }; -use ::syscall::CallerCtx; use core::{ mem, slice, @@ -806,6 +805,39 @@ impl KernelScheme for ProcScheme { // Return read events Ok(read * mem::size_of::()) } + Operation::AddrSpace { ref addrspace } => { + let OperationData::Offset(orig_offset) = self.handles.read().get(&id).ok_or(Error::new(EBADF))?.data else { + return Err(Error::new(EBADFD)); + }; + + // Output a list of grant descriptors, sufficient to allow relibc's fork() + // implementation to fmap MAP_SHARED grants. + let mut grants_read = 0; + + let mut dst = [GrantDesc::default(); 16]; + + for (dst, (grant_base, grant_info)) in dst.iter_mut().zip(addrspace.read().grants.iter().skip(orig_offset)) { + *dst = GrantDesc { + base: grant_base.start_address().data(), + size: grant_info.page_count() * PAGE_SIZE, + flags: grant_info.grant_flags(), + // The !0 is not a sentinel value; the availability of `offset` is + // indicated by the GRANT_SCHEME flag. + offset: grant_info.file_ref().map_or(!0, |f| f.base_offset as u64), + }; + grants_read += 1; + } + for (src, chunk) in dst.iter().take(grants_read).zip(buf.in_exact_chunks(mem::size_of::())) { + chunk.copy_exactly(src)?; + } + + match self.handles.write().get_mut(&id).ok_or(Error::new(EBADF))?.data { + OperationData::Offset(ref mut offset) => *offset = dbg!(dbg!(*offset) + dbg!(grants_read)), + _ => return Err(Error::new(EBADFD)), + }; + + Ok(grants_read * mem::size_of::()) + } Operation::Name => read_from(buf, context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().name.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) => { @@ -1131,6 +1163,7 @@ impl KernelScheme for ProcScheme { } Operation::AddrSpace { ref addrspace } => { let addrspace_clone = Arc::clone(addrspace); + const GRANT_FD_PREFIX: &[u8] = b"grant-fd-"; let operation = match buf { // TODO: Better way to obtain new empty address spaces, perhaps using SYS_OPEN. But @@ -1139,6 +1172,21 @@ impl KernelScheme for ProcScheme { b"exclusive" => Operation::AddrSpace { addrspace: addrspace.write().try_clone(addrspace_clone)? }, b"mmap-min-addr" => Operation::MmapMinAddr(Arc::clone(addrspace)), + _ if buf.starts_with(GRANT_FD_PREFIX) => { + let string = core::str::from_utf8(&buf[GRANT_FD_PREFIX.len()..]).map_err(|_| Error::new(EINVAL))?; + let page_addr = usize::from_str_radix(string, 16).map_err(|_| Error::new(EINVAL))?; + + if page_addr % PAGE_SIZE != 0 { + return Err(Error::new(EINVAL)); + } + + let page = Page::containing_address(VirtualAddress::new(page_addr)); + + match addrspace.read().grants.contains(page).ok_or(Error::new(EINVAL))? { + (_, info) => return Ok(OpenResult::External(info.file_ref().map(|r| Arc::clone(&r.description)).ok_or(Error::new(EBADF))?)), + } + } + _ => return Err(Error::new(EINVAL)), }; diff --git a/syscall b/syscall index d587771fc3..ff149946c0 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit d587771fc36eabc07472ad6bb0f00b2d2a931711 +Subproject commit ff149946c07628dde39db05997e221b5024ccb95