From ad78dcc5a1b1097988935e290f5f62f5e451f2b4 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 30 Jun 2023 18:24:12 +0200 Subject: [PATCH] WIP: Rudimentary MAP_SHARED fmap implementation. --- src/context/context.rs | 35 +++++++++++++++++++++++++------ src/context/memory.rs | 38 ++++++++++++++++++++++++++++++---- src/context/mod.rs | 2 +- src/context/switch.rs | 6 +++--- src/scheme/mod.rs | 4 ++++ src/scheme/user.rs | 47 ++++++++++++++++++++++++++++++++++++++---- src/syscall/process.rs | 2 +- 7 files changed, 115 insertions(+), 19 deletions(-) diff --git a/src/context/context.rs b/src/context/context.rs index bcd42f65..be2e8189 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -1,3 +1,4 @@ +use alloc::sync::Weak; use alloc::{ boxed::Box, collections::VecDeque, @@ -28,11 +29,13 @@ use crate::syscall::flag::{SIG_DFL, SigActionFlags}; /// Unique identifier for a context (i.e. `pid`). use ::core::sync::atomic::AtomicUsize; + +use super::memory::FmapCtxt; int_like!(ContextId, AtomicContextId, usize, AtomicUsize); /// The status of a context - used for scheduling /// See `syscall::process::waitpid` and the `sync` module for examples of usage -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug)] pub enum Status { Runnable, @@ -49,9 +52,19 @@ pub enum Status { Stopped(usize), Exited(usize), } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] + +impl Status { + pub fn is_runnable(&self) -> bool { + matches!(self, Self::Runnable) + } + pub fn is_soft_blocked(&self) -> bool { + matches!(self, Self::Blocked) + } +} + +#[derive(Clone, Debug)] pub enum HardBlockedReason { - AwaitingMmap, + AwaitingMmap { ctxt: Arc<FmapCtxt>, finished: Option<Frame> }, // TODO: PageFaultOom? // TODO: NotYetStarted/ManuallyBlocked (when new contexts are created) // TODO: ptrace_stop? @@ -174,7 +187,7 @@ impl ContextSnapshot { ens: context.ens, sigmask: context.sigmask, umask: context.umask, - status: context.status, + status: context.status.clone(), status_reason: context.status_reason, running: context.running, cpu_id: context.cpu_id, @@ -329,7 +342,7 @@ impl Context { /// Block the context, and return true if it was runnable before being blocked pub fn block(&mut self, reason: &'static str) -> bool { - if self.status == Status::Runnable { + if self.status.is_runnable() { self.status = Status::Blocked; self.status_reason = reason; true @@ -338,9 +351,19 @@ impl Context { } } + pub fn hard_block(&mut self, reason: HardBlockedReason) -> bool { + if self.status.is_runnable() { + self.status = Status::HardBlocked { reason }; + + true + } else { + false + } + } + /// Unblock context, and return true if it was blocked before being marked runnable pub fn unblock(&mut self) -> bool { - if self.status == Status::Blocked { + if self.status.is_soft_blocked() { self.status = Status::Runnable; self.status_reason = ""; diff --git a/src/context/memory.rs b/src/context/memory.rs index 3dd8140e..e00a2270 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -15,7 +15,9 @@ use crate::arch::paging::PAGE_SIZE; use crate::memory::{Enomem, Frame, get_page_info, PageInfo}; use crate::paging::mapper::{Flusher, InactiveFlusher, PageFlushAll}; use crate::paging::{KernelMapper, Page, PageFlags, PageMapper, RmmA, TableKind, VirtualAddress}; +use crate::scheme; +use super::context::HardBlockedReason; use super::file::FileDescription; pub const MMAP_MIN_DEFAULT: usize = PAGE_SIZE; @@ -1129,13 +1131,13 @@ pub unsafe fn copy_frame_to_frame_directly(dst: Frame, src: Frame) { } pub fn try_correcting_page_tables(faulting_page: Page, access: AccessMode) -> Result<(), PfError> { - let Ok(addr_space) = AddrSpace::current() else { + let Ok(addr_space_lock) = AddrSpace::current() else { log::debug!("User page fault without address space being set."); return Err(PfError::Segv); }; - let mut addr_space = addr_space.write(); - let addr_space = &mut *addr_space; + let mut addr_space_guard = addr_space_lock.write(); + let mut addr_space = &mut *addr_space_guard; let Some((grant_base, grant_info)) = addr_space.grants.contains(faulting_page) else { log::debug!("Lacks grant"); @@ -1232,7 +1234,35 @@ pub fn try_correcting_page_tables(faulting_page: Page, access: AccessMode) -> Re map_zeroed(&mut guard.table.utable, src_page, grant_flags, access == AccessMode::Write)? } } - Provider::FmapBorrowed { ref fmap } => todo!(), + Provider::FmapBorrowed { ref fmap } => { + let ctxt = Arc::clone(fmap); + let flags = map_flags(grant_info.flags()); + drop(addr_space); + + let (scheme_id, scheme_number) = match ctxt.file_ref.description.read() { + ref desc => (desc.scheme, desc.number), + }; + let user_inner = scheme::schemes() + .get(scheme_id).and_then(|s| s.as_user_inner().transpose().ok().flatten()) + .ok_or(PfError::Segv)?; + + let offset = ctxt.file_ref.base_offset as u64 + (pages_from_grant_start * PAGE_SIZE) as u64; + user_inner.request_fmap(scheme_number, offset, 1, flags).unwrap(); + + let context_lock = super::current().map_err(|_| PfError::NonfatalInternalError)?; + context_lock.write().hard_block(HardBlockedReason::AwaitingMmap { ctxt, finished: None }); + + unsafe { super::switch(); } + + let super::Status::HardBlocked { reason: HardBlockedReason::AwaitingMmap { finished: Some(frame), .. } } = core::mem::replace(&mut context_lock.write().status, super::Status::Runnable) else { + return Err(PfError::NonfatalInternalError); + }; + + addr_space_guard = addr_space_lock.write(); + addr_space = &mut *addr_space_guard; + + frame + } }; if super::context_id().into() == 3 && debug { diff --git a/src/context/mod.rs b/src/context/mod.rs index e08bd71f..40e17f54 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -28,7 +28,7 @@ mod arch; mod arch; /// Context struct -mod context; +pub mod context; /// Context list mod list; diff --git a/src/context/switch.rs b/src/context/switch.rs index bca5f7ac..e5a13de4 100644 --- a/src/context/switch.rs +++ b/src/context/switch.rs @@ -64,12 +64,12 @@ unsafe fn update_runnable(context: &mut Context, cpu_id: usize) -> bool { } // Unblock when there are pending signals - if context.status == Status::Blocked && !context.pending.is_empty() { + if context.status.is_soft_blocked() && !context.pending.is_empty() { context.unblock(); } // Wake from sleep - if context.status == Status::Blocked && context.wake.is_some() { + if context.status.is_soft_blocked() && context.wake.is_some() { let wake = context.wake.expect("context::switch: wake not set"); let current = time::monotonic(); @@ -80,7 +80,7 @@ unsafe fn update_runnable(context: &mut Context, cpu_id: usize) -> bool { } // Switch to context if it needs to run - context.status == Status::Runnable + context.status.is_runnable() } struct SwitchResult { diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index e8b4ed1e..bda596f7 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -37,6 +37,7 @@ use self::root::RootScheme; use self::serio::SerioScheme; use self::sys::SysScheme; use self::time::TimeScheme; +use self::user::UserInner; /// When compiled with the "acpi" feature - `acpi:` - allows drivers to read a limited set of ACPI tables. #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] @@ -335,6 +336,9 @@ pub trait KernelScheme: Scheme + Send + Sync + 'static { fn kfstatvfs(&self, id: usize, buf: UserSliceWo) -> Result<usize> { Err(Error::new(EBADF)) } + + // TODO: This demonstrates why we need to transition away from a dyn trait. + fn as_user_inner(&self) -> Option<Result<Arc<UserInner>>> { None } } #[derive(Debug)] diff --git a/src/scheme/user.rs b/src/scheme/user.rs index e0227f4c..9539d055 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -8,10 +8,12 @@ use core::{mem, usize}; use core::convert::TryFrom; use spin::{Mutex, RwLock}; -use crate::context::{self, Context, BorrowedHtBuf}; +use crate::context::context::HardBlockedReason; +use crate::context::{self, Context, BorrowedHtBuf, Status}; use crate::context::file::FileDescription; use crate::context::memory::{AddrSpace, DANGLING, Grant, GrantFileRef, PageSpan, MmapMode, page_flags, FmapCtxt}; use crate::event; +use crate::memory::Frame; use crate::paging::{PAGE_SIZE, Page, VirtualAddress}; use crate::scheme::{AtomicSchemeId, SchemeId}; use crate::sync::{WaitQueue, WaitMap}; @@ -34,6 +36,7 @@ pub struct UserInner { context: Weak<RwLock<Context>>, todo: WaitQueue<Packet>, done: WaitMap<u64, Response>, + fmap: Mutex<BTreeMap<u64, Weak<RwLock<Context>>>>, unmounting: AtomicBool, } pub enum Response { @@ -56,6 +59,7 @@ impl UserInner { todo: WaitQueue::new(), done: WaitMap::new(), unmounting: AtomicBool::new(false), + fmap: Mutex::new(BTreeMap::new()), } } @@ -341,6 +345,21 @@ impl UserInner { Ok(packets_read * mem::size_of::<Packet>()) } + pub fn request_fmap(&self, id: usize, offset: u64, required_page_count: usize, flags: MapFlags) -> Result<()> { + self.todo.send(Packet { + id: self.next_id(), + pid: context::context_id().into(), + a: KSMSG_MMAP, + b: id, + c: flags.bits(), + d: required_page_count, + uid: offset as u32, + gid: (offset >> 32) as u32, + }); + event::trigger(self.root_id, self.handle_id, EVENT_READ); + + Ok(()) + } fn handle_packet(&self, packet: &Packet) -> Result<()> { if packet.id == 0 { // TODO: Simplify logic by using SKMSG with packet.id being ignored? @@ -360,15 +379,31 @@ impl UserInner { self.done.send(packet.id, Response::Fd(desc)); } SKMSG_PROVIDE_MMAP => { + log::info!("PROVIDE_MAP {:?}", packet); let offset = u64::from(packet.uid) | (u64::from(packet.gid) << 32); if offset % PAGE_SIZE as u64 != 0 { - return Err(Error::new(EINVAL)); + return dbg!(Err(Error::new(EINVAL))); + } + + let base_addr = VirtualAddress::new(packet.c); + if base_addr.data() % PAGE_SIZE != 0 { + return dbg!(Err(Error::new(EINVAL))); } - let page_count = packet.c; + let page_count = packet.d; + + if page_count != 1 { return dbg!(Err(Error::new(EINVAL))); } + let context = self.fmap.lock().remove(&packet.id).ok_or(Error::new(EINVAL))?.upgrade().ok_or(Error::new(ESRCH))?; + + let (frame, _) = AddrSpace::current()?.read().table.utable.translate(base_addr).ok_or(Error::new(EFAULT))?; + + let mut context = context.write(); + match context.status { + Status::HardBlocked { reason: HardBlockedReason::AwaitingMmap { ref mut finished, .. } } => *finished = Some(Frame::containing_address(frame)), + _ => (), + } - todo!("respond to SKMSG_PROVIDE_MMAP") } _ => return Err(Error::new(EINVAL)), } @@ -752,6 +787,10 @@ impl KernelScheme for UserScheme { inner.fmap_inner(Arc::clone(addr_space), file, map) } + + fn as_user_inner(&self) -> Option<Result<Arc<UserInner>>> { + Some(self.inner.upgrade().ok_or(Error::new(ENODEV))) + } } #[derive(PartialEq)] diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 1284d317..4737b162 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -487,7 +487,7 @@ pub fn waitpid(pid: ContextId, status_ptr: Option<UserSliceWo>, flags: WaitFlags println!("TODO: Hack for rustc - changing ppid of {} from {} to {}", context.id.into(), context.ppid.into(), ppid.into()); context.ppid = ppid; //return Err(Error::new(ECHILD)); - Some(context.status) + Some(context.status.clone()) } else { None } -- GitLab