From aca1b9058b0f74bcf39cf0791316b07000ba09b2 Mon Sep 17 00:00:00 2001 From: Josh Junon Date: Mon, 20 Jan 2025 04:04:08 +0100 Subject: [PATCH] kernel: refactor to use tabs kernel-wide --- oro-arch-x86_64/src/init.rs | 20 +- oro-arch-x86_64/src/syscall.rs | 2 +- oro-kernel/src/id.rs | 22 -- oro-kernel/src/iface/kernel/mod.rs | 2 +- oro-kernel/src/iface/kernel/thread_v0.rs | 150 +++++----- .../src/iface/root_ring/boot_vbuf_v0.rs | 3 +- .../src/iface/root_ring/debug_out_v0.rs | 3 +- oro-kernel/src/instance.rs | 80 ++---- oro-kernel/src/interface.rs | 247 ++-------------- oro-kernel/src/lib.rs | 69 +++-- oro-kernel/src/module.rs | 56 +--- oro-kernel/src/port.rs | 111 -------- oro-kernel/src/registry.rs | 188 ------------ oro-kernel/src/ring.rs | 118 ++++---- oro-kernel/src/scheduler.rs | 46 +-- oro-kernel/src/syscall.rs | 268 ++++++++++++++++++ oro-kernel/src/tab.rs | 54 ++-- oro-kernel/src/table.rs | 15 - oro-kernel/src/thread.rs | 51 ++-- 19 files changed, 575 insertions(+), 930 deletions(-) delete mode 100644 oro-kernel/src/id.rs delete mode 100644 oro-kernel/src/port.rs delete mode 100644 oro-kernel/src/registry.rs create mode 100644 oro-kernel/src/syscall.rs diff --git a/oro-arch-x86_64/src/init.rs b/oro-arch-x86_64/src/init.rs index 6be84605..7b65e068 100644 --- a/oro-arch-x86_64/src/init.rs +++ b/oro-arch-x86_64/src/init.rs @@ -71,7 +71,7 @@ pub unsafe fn initialize_primary(lapic: Lapic) { let root_ring = kernel.state().root_ring(); - 'module: while next != 0 { + while next != 0 { let Some(module) = Phys::from_address_unchecked(next).as_ref::() else { @@ -107,9 +107,7 @@ pub unsafe fn initialize_primary(lapic: Lapic) { let module_handle = Module::new(id.clone()).expect("failed to create root ring module"); - let entry_point = { - let module_lock = module_handle.lock(); - + let entry_point = module_handle.with(|module_lock| { let mapper = module_lock.mapper(); let elf_base = Phys::from_address_unchecked(module.base).as_ptr_unchecked::(); @@ -124,20 +122,20 @@ pub unsafe fn initialize_primary(lapic: Lapic) { for segment in elf.segments() { let mapper_segment = match segment.ty() { - ElfSegmentType::Ignored => continue 'module, + ElfSegmentType::Ignored => return None, ElfSegmentType::Invalid { flags, ptype } => { dbg_err!( "root ring module {id} has invalid segment; skipping: \ ptype={ptype:?} flags={flags:?}", ); - continue 'module; + return None; } ElfSegmentType::ModuleCode => AddressSpaceLayout::user_code(), ElfSegmentType::ModuleData => AddressSpaceLayout::user_data(), ElfSegmentType::ModuleRoData => AddressSpaceLayout::user_rodata(), ty => { dbg_err!("root ring module {id} has invalid segment {ty:?}; skipping",); - continue 'module; + return None; } }; @@ -193,10 +191,14 @@ pub unsafe fn initialize_primary(lapic: Lapic) { } } - elf.entry_point() + Some(elf.entry_point()) + }); + + let Some(entry_point) = entry_point else { + continue; }; - let instance = Instance::new(&module_handle, &root_ring) + let instance = Instance::new(&module_handle, root_ring) .expect("failed to create root ring instance"); // Create a thread for the entry point. diff --git a/oro-arch-x86_64/src/syscall.rs b/oro-arch-x86_64/src/syscall.rs index 968689d0..c668ae0e 100644 --- a/oro-arch-x86_64/src/syscall.rs +++ b/oro-arch-x86_64/src/syscall.rs @@ -2,7 +2,7 @@ use core::arch::{asm, naked_asm}; -use oro_kernel::{interface::SystemCallRequest, scheduler::Switch}; +use oro_kernel::{scheduler::Switch, syscall::SystemCallRequest}; use oro_mem::mapper::AddressSegment; use oro_sync::Lock; diff --git a/oro-kernel/src/id.rs b/oro-kernel/src/id.rs deleted file mode 100644 index 363c3e0a..00000000 --- a/oro-kernel/src/id.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! Globally unique identifier allocator. - -use core::sync::atomic::{AtomicU64, Ordering}; - -/// Static, system-wide monotonically increasing resource counter. -/// -/// Used for a variety of resources; all resources in the system are guaranteed -/// to have a unique ID, even across resource types. -/// -/// Always starts at `0x0000_0001_0000_0000`. -static COUNTER: AtomicU64 = AtomicU64::new(0x0000_0001_0000_0000); - -/// Allocates a new globally unique identifier. -/// -/// Guaranteed to be unique across all cores in the system, -/// and monotonically increasing. This function is lock-free. -/// -/// Always starts at `0x0000_0001_0000_0000`. Guaranteed never to return 0. -#[inline] -pub fn allocate() -> u64 { - COUNTER.fetch_add(1, Ordering::Relaxed) -} diff --git a/oro-kernel/src/iface/kernel/mod.rs b/oro-kernel/src/iface/kernel/mod.rs index 85e6d4c4..09a4d1c8 100644 --- a/oro-kernel/src/iface/kernel/mod.rs +++ b/oro-kernel/src/iface/kernel/mod.rs @@ -2,7 +2,7 @@ //! //! Always available, regardless of the caller's ring. -use crate::{arch::Arch, interface::InterfaceResponse, tab::Tab, thread::Thread}; +use crate::{arch::Arch, syscall::InterfaceResponse, tab::Tab, thread::Thread}; mod thread_v0; diff --git a/oro-kernel/src/iface/kernel/thread_v0.rs b/oro-kernel/src/iface/kernel/thread_v0.rs index 3503b37c..88776240 100644 --- a/oro-kernel/src/iface/kernel/thread_v0.rs +++ b/oro-kernel/src/iface/kernel/thread_v0.rs @@ -1,12 +1,11 @@ //! Implements version 0 of the thread control interface. -use oro_sync::Lock; use oro_sysabi::{key, syscall::Error as SysError}; use super::KernelInterface; use crate::{ arch::Arch, - interface::{InterfaceResponse, SystemCallResponse}, + syscall::{InterfaceResponse, SystemCallResponse}, tab::Tab, thread::{ChangeStateError, RunState, Thread}, }; @@ -27,64 +26,63 @@ pub enum Error { #[repr(transparent)] pub struct ThreadV0; -/// Given a thread, index and key, checks to see if the index (thread ID) -/// is `0` (indicating 'self') or another thread, attempts to look up the -/// thread ID in the instance's thread table, and then executes the given -/// match block with the target thread (either self or the found thread). -macro_rules! with_thread_id { - ($thread:expr, $index:expr, ($source_id:ident, $thr_target:ident) for match $key:ident { $($tt:tt)* }) => { - if $index == 0 { - { - $thread.with_mut(|$thr_target| { - let $source_id = $thr_target.id(); - match $key { - $( $tt )* - } - }) - } +/// Resolves the target thread from the given index, +/// checking that the caller has permission to access it. +macro_rules! resolve_target { + ($thread:expr, $index:expr) => {{ + let thread = $thread; + let index = $index; + if index == 0 || index == thread.id() { + thread.clone() } else { - $thread.with(|thread_lock| { - let instance = thread_lock.instance(); - let instance_lock = instance.lock(); - let threads = instance_lock.threads(); - if let Some(other_thread) = threads.get($index) { - { - let $source_id = thread_lock.id(); - other_thread.with_mut(|$thr_target| { - match $key { - $( $tt )* - } - }) + match crate::tab::get().lookup::>(index) { + Some(t) => { + if t.with(|t| t.ring().id()) != thread.with(|t| t.ring().id()) { + return InterfaceResponse::Immediate(SystemCallResponse { + error: SysError::BadIndex, + ret: 0, + }); } - } else { - InterfaceResponse::Immediate(SystemCallResponse { + + t + } + None => { + return InterfaceResponse::Immediate(SystemCallResponse { error: SysError::BadIndex, ret: 0, - }) + }); } - }) + } } - }; + }}; } impl KernelInterface for ThreadV0 { const TYPE_ID: u64 = oro_sysabi::id::iface::KERNEL_THREAD_V0; fn get(thread: &Tab>, index: u64, key: u64) -> InterfaceResponse { - with_thread_id!(thread, index, (_caller_id, target) for match key { - key!("id") => InterfaceResponse::Immediate(SystemCallResponse { - error: SysError::Ok, - ret: target.id(), - }), - key!("status") => InterfaceResponse::Immediate(SystemCallResponse { - error: SysError::Ok, - ret: target.run_state() as u64, - }), - _ => InterfaceResponse::Immediate(SystemCallResponse { - error: SysError::BadKey, - ret: 0, - }), - }) + let target = resolve_target!(thread, index); + + match key { + key!("id") => { + InterfaceResponse::Immediate(SystemCallResponse { + error: SysError::Ok, + ret: target.id(), + }) + } + key!("status") => { + InterfaceResponse::Immediate(SystemCallResponse { + error: SysError::Ok, + ret: target.with(|t| t.run_state()) as u64, + }) + } + _ => { + InterfaceResponse::Immediate(SystemCallResponse { + error: SysError::BadKey, + ret: 0, + }) + } + } } fn set( @@ -93,11 +91,15 @@ impl KernelInterface for ThreadV0 { key: u64, value: u64, ) -> InterfaceResponse { - with_thread_id!(thread, index, (caller_id, target) for match key { - key!("id") => InterfaceResponse::Immediate(SystemCallResponse { - error: SysError::ReadOnly, - ret: 0, - }), + let target = resolve_target!(thread, index); + + match key { + key!("id") => { + InterfaceResponse::Immediate(SystemCallResponse { + error: SysError::ReadOnly, + ret: 0, + }) + } key!("status") => { let Ok(new_state) = RunState::try_from(value) else { return InterfaceResponse::Immediate(SystemCallResponse { @@ -106,25 +108,31 @@ impl KernelInterface for ThreadV0 { }); }; - match target.transition_to(caller_id, new_state) { - Ok(None) => InterfaceResponse::Immediate(SystemCallResponse { - error: SysError::Ok, - ret: 0, - }), + match target.with_mut(|t| t.transition_to(thread.id(), new_state)) { + Ok(None) => { + InterfaceResponse::Immediate(SystemCallResponse { + error: SysError::Ok, + ret: 0, + }) + } Ok(Some(transition)) => InterfaceResponse::Pending(transition), - Err(e) => InterfaceResponse::Immediate(SystemCallResponse { - error: SysError::InterfaceError, - ret: match e { - ChangeStateError::Race => Error::Race, - ChangeStateError::Terminated => Error::Terminated, - } as u64, - }), + Err(e) => { + InterfaceResponse::Immediate(SystemCallResponse { + error: SysError::InterfaceError, + ret: match e { + ChangeStateError::Race => Error::Race, + ChangeStateError::Terminated => Error::Terminated, + } as u64, + }) + } } - }, - _ => InterfaceResponse::Immediate(SystemCallResponse { - error: SysError::BadKey, - ret: 0, - }), - }) + } + _ => { + InterfaceResponse::Immediate(SystemCallResponse { + error: SysError::BadKey, + ret: 0, + }) + } + } } } diff --git a/oro-kernel/src/iface/root_ring/boot_vbuf_v0.rs b/oro-kernel/src/iface/root_ring/boot_vbuf_v0.rs index 1846b55f..22895aba 100644 --- a/oro-kernel/src/iface/root_ring/boot_vbuf_v0.rs +++ b/oro-kernel/src/iface/root_ring/boot_vbuf_v0.rs @@ -34,7 +34,8 @@ use oro_sysabi::{key, syscall::Error as SysError}; use crate::{ arch::Arch, - interface::{Interface, InterfaceResponse, SystemCallResponse}, + interface::Interface, + syscall::{InterfaceResponse, SystemCallResponse}, tab::Tab, thread::Thread, }; diff --git a/oro-kernel/src/iface/root_ring/debug_out_v0.rs b/oro-kernel/src/iface/root_ring/debug_out_v0.rs index a153d2a7..784be41c 100644 --- a/oro-kernel/src/iface/root_ring/debug_out_v0.rs +++ b/oro-kernel/src/iface/root_ring/debug_out_v0.rs @@ -21,7 +21,8 @@ use oro_sysabi::{key, syscall::Error as SysError}; use crate::{ arch::Arch, - interface::{Interface, InterfaceResponse, SystemCallResponse}, + interface::Interface, + syscall::{InterfaceResponse, SystemCallResponse}, tab::Tab, thread::Thread, }; diff --git a/oro-kernel/src/instance.rs b/oro-kernel/src/instance.rs index cf6c2081..597b7f5c 100644 --- a/oro-kernel/src/instance.rs +++ b/oro-kernel/src/instance.rs @@ -1,19 +1,11 @@ //! Module instance types and functionality. -use oro_mem::{ - alloc::{ - sync::{Arc, Weak}, - vec::Vec, - }, - mapper::{AddressSegment, AddressSpace as _, MapError}, -}; -use oro_sync::{Lock, ReentrantMutex}; +use oro_mem::mapper::{AddressSegment, AddressSpace as _, MapError}; use crate::{ AddressSpace, Kernel, UserHandle, arch::{Arch, InstanceHandle}, module::Module, - port::Port, ring::Ring, tab::Tab, table::Table, @@ -51,20 +43,16 @@ use crate::{ /// bootstrap the rest of the system as they see fit. #[non_exhaustive] pub struct Instance { - /// The module instance ID. - id: u64, /// The module from which this instance was spawned. /// /// Strong reference to prevent the module from being /// deallocated while the instance is still alive, which would /// otherwise reclaim the executable memory pages and wreak havoc. - module: Arc>>, + module: Tab>, /// The ring on which this instance resides. - ring: Weak>>, + ring: Tab>, /// The thread list for the instance. pub(super) threads: Table>>, - /// The port list for the instance. - ports: Vec>>, /// The instance's architecture handle. handle: A::InstanceHandle, } @@ -73,54 +61,45 @@ impl Instance { /// Creates a new instance, allocating a new mapper. /// /// Notably, this does **not** spawn any threads. - pub fn new( - module: &Arc>>, - ring: &Arc>>, - ) -> Result>, MapError> { - let id = crate::id::allocate(); - + pub fn new(module: &Tab>, ring: &Tab>) -> Result, MapError> { let mapper = AddressSpace::::new_user_space(Kernel::::get().mapper()) .ok_or(MapError::OutOfMemory)?; let handle = A::InstanceHandle::new(mapper)?; // Apply the ring mapper overlay to the instance. - AddressSpace::::sysabi() - .apply_user_space_shallow(handle.mapper(), ring.lock().mapper())?; + ring.with(|ring| { + AddressSpace::::sysabi().apply_user_space_shallow(handle.mapper(), ring.mapper()) + })?; // Apply the module's read-only mapper overlay to the instance. - AddressSpace::::user_rodata() - .apply_user_space_shallow(handle.mapper(), module.lock().mapper())?; - - let r = Arc::new(ReentrantMutex::new(Self { - id, - module: module.clone(), - ring: Arc::downgrade(ring), - threads: Table::new(), - ports: Vec::new(), - handle, - })); + module.with(|module| { + AddressSpace::::user_rodata() + .apply_user_space_shallow(handle.mapper(), module.mapper()) + })?; + + let tab = crate::tab::get() + .add(Self { + module: module.clone(), + ring: ring.clone(), + threads: Table::new(), + handle, + }) + .ok_or(MapError::OutOfMemory)?; - ring.lock().instances_mut().push(r.clone()); - module.lock().instances.push(Arc::downgrade(&r)); + ring.with_mut(|ring| ring.instances_mut().push(tab.clone())); - Ok(r) - } - - /// Returns the instance ID. - #[must_use] - pub fn id(&self) -> u64 { - self.id + Ok(tab) } /// The handle to the module from which this instance was spawned. - pub fn module(&self) -> Arc>> { - self.module.clone() + pub fn module(&self) -> &Tab> { + &self.module } - /// The weak handle to the ring on which this instance resides. - pub fn ring(&self) -> Weak>> { - self.ring.clone() + /// The handle to the ring on which this instance resides. + pub fn ring(&self) -> &Tab> { + &self.ring } /// Gets a handle to the list of threads for this instance. @@ -128,11 +107,6 @@ impl Instance { &self.threads } - /// Gets a handle to the list of ports for this instance. - pub fn ports(&self) -> &[Arc>] { - &self.ports - } - /// Returns the instance's address space handle. #[must_use] pub fn mapper(&self) -> &UserHandle { diff --git a/oro-kernel/src/interface.rs b/oro-kernel/src/interface.rs index b5f988f1..4e859191 100644 --- a/oro-kernel/src/interface.rs +++ b/oro-kernel/src/interface.rs @@ -1,20 +1,16 @@ //! Types and traits for Oro interfaces, exposed by modules and the kernel. -use core::{ - cell::UnsafeCell, - marker::PhantomData, - mem::MaybeUninit, - sync::atomic::{ - AtomicU8, - Ordering::{Acquire, Relaxed, Release}, - }, -}; +use core::marker::PhantomData; -use oro_mem::alloc::sync::Arc; -use oro_sync::Lock; +use oro_mem::alloc::boxed::Box; use oro_sysabi::syscall::Error as SysError; -use crate::{arch::Arch, tab::Tab, thread::Thread}; +use crate::{ + arch::Arch, + syscall::{InterfaceResponse, SystemCallResponse}, + tab::Tab, + thread::Thread, +}; /// Implements an interface, which is a flat namespace of `(index, flay_keyvals)`. /// @@ -48,43 +44,36 @@ pub trait Interface: Send + Sync { /// Implements a scoped interface wrapper, which is an [`Interface`] that is only accessible /// within a specific ring. -pub struct RingInterface> { +pub struct RingInterface { /// The interface. - interface: I, + interface: Box>, /// The ring ID. ring_id: u64, #[doc(hidden)] _arch: PhantomData, } -impl> RingInterface { +impl RingInterface { /// Creates a new ring interface. - pub fn new(interface: I, ring_id: u64) -> Self { + pub fn new + 'static>(interface: I, ring_id: u64) -> Self { Self { - interface, + interface: Box::new(interface), ring_id, _arch: PhantomData, } } } -impl> Interface for RingInterface { +impl Interface for RingInterface { #[inline] fn type_id(&self) -> u64 { self.interface.type_id() } fn get(&self, thread: &Tab>, index: u64, key: u64) -> InterfaceResponse { - let ring_id = { - let instance = thread.with(|t| t.instance()); - let instance_lock = instance.lock(); - let ring = instance_lock.ring(); - if let Some(ring) = ring.upgrade() { - ring.lock().id() - } else { - 0 - } - }; + let ring_id = thread + .with(|t| t.instance().clone()) + .with(|i| i.ring().id()); if ring_id != self.ring_id { return InterfaceResponse::Immediate(SystemCallResponse { @@ -97,16 +86,7 @@ impl> Interface for RingInterface { } fn set(&self, thread: &Tab>, index: u64, key: u64, value: u64) -> InterfaceResponse { - let ring_id = { - let instance = thread.with(|t| t.instance()); - let instance_lock = instance.lock(); - let ring = instance_lock.ring(); - if let Some(ring) = ring.upgrade() { - ring.lock().id() - } else { - 0 - } - }; + let ring_id = thread.with(|t| t.ring()).id(); if ring_id != self.ring_id { return InterfaceResponse::Immediate(SystemCallResponse { @@ -118,194 +98,3 @@ impl> Interface for RingInterface { self.interface.set(thread, index, key, value) } } - -/// Response from an interface after handling a system call. -/// -/// When performing an [`Interface`] syscall operation, the interface can either -/// respond immediately, or defer the response to a later time. In the latter case, -/// a pollable handle is returned. -pub enum InterfaceResponse { - /// The interface has handled the request and the response is ready. - Immediate(SystemCallResponse), - /// The interface has received the request, but the response is not ready yet. - Pending(InFlightSystemCallHandle), -} - -/// The producer side of an in-flight system call. -/// -/// Interfaces should hold onto this handle when they defer a system call response, -/// allowing to submit the response at a later time. -#[repr(transparent)] -pub struct InFlightSystemCall(Arc); - -/// Inner shared state for an in-flight system call. -struct InFlightSystemCallInner { - /// The response is ready. Used as an atomic rather than an `Option`. - state: AtomicU8, - /// The response data; only valid if `state` is [`InFlightState::Ready`]. - response: UnsafeCell>, -} - -// SAFETY: We strictly control access to the inner state and control exactly -// SAFETY: how it is used, and can guarantee that it is safe to share across -// SAFETY: threads. Any misbehavior is a bug. -unsafe impl Sync for InFlightSystemCallInner {} -// SAFETY: The inner state is valid across threads, despite using an unsafe cell. -unsafe impl Send for InFlightSystemCallInner {} - -/// Indicates the state of the in-flight system call. -#[derive(Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum InFlightState { - /// Pending - Pending, - /// The response is ready. - Ready, - /// The caller has canceled the system call. - CallerCanceled, - /// The interface has canceled the system call. - InterfaceCanceled, - /// The interface serviced the response, and the caller has - /// already taken the response. This is considered an error. - Finished, -} - -/// The caller side of an in-flight system call. -pub struct InFlightSystemCallHandle(Arc); - -impl InFlightSystemCall { - /// Creates a new in-flight system call interface/caller pair. - /// - /// The interface should call this, hold on to the first element - /// (the interface side), and return the second element (the caller side) - /// via [`InterfaceResponse::Pending`]. - /// - /// Once the interface has a response, it can submit it via [`Self::submit`], - /// which consumes this handle and notifies the caller. - #[must_use] - pub fn new() -> (Self, InFlightSystemCallHandle) { - let inner = Arc::new(InFlightSystemCallInner { - state: AtomicU8::new(InFlightState::Pending as u8), - response: UnsafeCell::new(MaybeUninit::uninit()), - }); - - (Self(inner.clone()), InFlightSystemCallHandle(inner)) - } - - /// Checks if the caller canceled the system call. - /// - /// There's no need to check this before submitting a response; however, - /// if the caller is doing something long-running, it may be useful to - /// check, as if this returns `true`, the response will be dropped immediately. - #[inline] - #[must_use] - pub fn canceled(&self) -> bool { - self.0.state.load(Relaxed) == (InFlightState::CallerCanceled as u8) - } - - /// Submits a response to the in-flight system call, consuming the - /// interface side of the handle. - /// - /// **Note:** there is no guarantee that the response will be taken by the - /// caller; if the caller canceled the system call, the response will be - /// dropped. Further, the caller may cancel the system call after this - /// method is called, but before the response is taken. - /// - /// If the interface's processing of this sytem call request is long-winded, - /// and side effects are not a concern, it may be useful to check if the - /// system call was canceled before submitting the response via [`Self::canceled`]. - pub fn submit(self, response: SystemCallResponse) { - // SAFETY: We are the only ones that can write to the response, - // SAFETY: and it can only happen here, once. - unsafe { self.0.response.get().write(MaybeUninit::new(response)) }; - // NOTE(qix-): Even if the caller canceled the system call, this is - // NOTE(qix-): innocuous; it's going to get dropped anyway, and we - // NOTE(qix-): save a branch. - self.0.state.store(InFlightState::Ready as u8, Release); - } -} - -impl Drop for InFlightSystemCall { - fn drop(&mut self) { - self.0 - .state - .compare_exchange( - InFlightState::Pending as u8, - InFlightState::InterfaceCanceled as u8, - Relaxed, - Relaxed, - ) - .ok(); - } -} - -impl InFlightSystemCallHandle { - /// Get the current state of the in-flight system call. - #[inline] - #[must_use] - pub fn state(&self) -> InFlightState { - // SAFETY: We control the value entirely; we can transmute it back. - unsafe { ::core::mem::transmute::(self.0.state.load(Acquire)) } - } - - /// Try to take the response from the in-flight system call. - /// - /// - If the response is ready, it is returned as `Ok(Some(response))`. - /// - If the response is not ready, `Ok(None)` is returned. - /// - If the system call was canceled by the interface, `Err(InFlightState::InterfaceCanceled)` - /// is returned. - /// - Attempts to take the response after it has been taken by the caller will return - /// `Err(InFlightState::Finished)`. - #[inline] - pub fn try_take_response(&self) -> Result, InFlightState> { - match self.state() { - InFlightState::Pending => Ok(None), - InFlightState::CallerCanceled => unreachable!(), - InFlightState::Ready => { - // SAFETY: We are the only ones that can write to the response, - // SAFETY: and it can only happen once. - self.0.state.store(InFlightState::Finished as u8, Release); - Ok(Some(unsafe { self.0.response.get().read().assume_init() })) - } - status @ (InFlightState::Finished | InFlightState::InterfaceCanceled) => Err(status), - } - } -} - -impl Drop for InFlightSystemCallHandle { - fn drop(&mut self) { - self.0 - .state - .compare_exchange( - InFlightState::Pending as u8, - InFlightState::CallerCanceled as u8, - Relaxed, - Relaxed, - ) - .ok(); - } -} - -/// System call request data. -#[derive(Debug, Clone)] -pub struct SystemCallRequest { - /// The opcode. - pub opcode: u64, - /// The first argument. - pub arg1: u64, - /// The second argument. - pub arg2: u64, - /// The third argument. - pub arg3: u64, - /// The fourth argument. - pub arg4: u64, -} - -/// System call response data. -#[derive(Debug, Clone, Copy)] -pub struct SystemCallResponse { - /// The error code. - pub error: oro_sysabi::syscall::Error, - /// The return value. - pub ret: u64, -} diff --git a/oro-kernel/src/lib.rs b/oro-kernel/src/lib.rs index 3e42f860..21d81fec 100644 --- a/oro-kernel/src/lib.rs +++ b/oro-kernel/src/lib.rs @@ -29,33 +29,31 @@ #![cfg_attr(doc, feature(doc_cfg, doc_auto_cfg))] pub mod arch; -pub mod id; pub mod iface; pub mod instance; pub mod interface; pub mod module; -pub mod port; -pub mod registry; pub mod ring; pub mod scheduler; pub mod sync; +pub mod syscall; pub mod tab; pub mod table; pub mod thread; use core::mem::MaybeUninit; +use interface::RingInterface; use oro_macro::assert; use oro_mem::{ - alloc::{sync::Arc, vec::Vec}, + alloc::vec::Vec, global_alloc::GlobalPfa, mapper::{AddressSegment, AddressSpace as _, MapError}, pfa::Alloc, }; -use oro_sync::{Lock, ReentrantMutex, TicketMutex}; -use registry::{Registry, RootRegistry}; +use oro_sync::{Lock, TicketMutex}; -use self::{arch::Arch, interface::RingInterface, scheduler::Scheduler}; +use self::{arch::Arch, scheduler::Scheduler}; /// Core-local instance of the Oro kernel. /// @@ -212,10 +210,7 @@ pub struct KernelState { /// List of all threads. threads: TicketMutex>>>, /// The root ring. - root_ring: Arc>>, - /// The system-wide registry, loaded at boot with all of the "static" kernel - /// interfaces. - registry: Arc>>, + root_ring: tab::Tab>, } impl KernelState { @@ -236,55 +231,59 @@ impl KernelState { // SAFETY(qix-): Must be first, before anything else happens in the kernel. self::sync::install_dummy_kernel_id_fn(); - let registry = Arc::new(ReentrantMutex::new(RootRegistry::new_empty())); - - let root_ring = ring::Ring::::new_root(registry.clone())?; + let root_ring = ring::Ring::::new_root()?; // Install root ring interfaces. { - let mut registry = registry.lock(); - let ifaceid = registry.register_interface(Arc::new(RingInterface::new( - self::iface::root_ring::debug_out_v0::DebugOutV0::new(), - root_ring.lock().id(), - ))); + let ifaceid = tab::get() + .add(RingInterface::::new( + self::iface::root_ring::debug_out_v0::DebugOutV0::new(), + root_ring.id(), + )) + .expect("out of memory"); + // XXX(qix-): DEBUG - ::oro_debug::dbg!("registered DebugLogV0: {ifaceid}"); + ::oro_debug::dbg!("registered DebugLogV0: {}", ifaceid.id()); + + root_ring.with_mut(|root_ring| { + root_ring.interfaces_mut().push(ifaceid); + }); #[cfg(feature = "boot-vbuf-v0")] { - let ifaceid = registry.register_interface(Arc::new(RingInterface::new( - self::iface::root_ring::boot_vbuf_v0::BootVbufV0::new(), - root_ring.lock().id(), - ))); + let ifaceid = tab::get() + .add(RingInterface::::new( + self::iface::root_ring::boot_vbuf_v0::BootVbufV0::new(), + root_ring.id(), + )) + .expect("out of memory"); + // XXX(qix-): DEBUG - ::oro_debug::dbg!("registered BootVbufV0: {ifaceid}"); + ::oro_debug::dbg!("registered BootVbufV0: {}", ifaceid.id()); + + root_ring.with_mut(|root_ring| { + root_ring.interfaces_mut().push(ifaceid); + }); } } this.write(Self { root_ring, threads: TicketMutex::default(), - registry, }); Ok(()) } /// Returns a handle to the root ring. - pub fn root_ring(&'static self) -> Arc>> { - self.root_ring.clone() + pub fn root_ring(&self) -> &tab::Tab> { + &self.root_ring } /// Returns a reference to the mutex-guarded list of threads. - pub fn threads(&'static self) -> &'static impl Lock>>> { + pub fn threads(&self) -> &impl Lock>>> { &self.threads } - - /// Returns a reference to the root registry. - #[inline] - pub fn registry(&self) -> &Arc>> { - &self.registry - } } /// Helper trait association type for `Arch::AddrSpace`. diff --git a/oro-kernel/src/module.rs b/oro-kernel/src/module.rs index 97b215f4..2a45b5ce 100644 --- a/oro-kernel/src/module.rs +++ b/oro-kernel/src/module.rs @@ -3,15 +3,11 @@ use oro_id::{Id, IdType}; use oro_macro::assert; use oro_mem::{ - alloc::{ - sync::{Arc, Weak}, - vec::Vec, - }, + alloc::vec::Vec, mapper::{AddressSpace as _, MapError}, }; -use oro_sync::{Lock, ReentrantMutex}; -use crate::{AddressSpace, UserHandle, arch::Arch, instance::Instance}; +use crate::{AddressSpace, UserHandle, arch::Arch, tab::Tab}; /// A singular executable module. /// @@ -23,13 +19,9 @@ use crate::{AddressSpace, UserHandle, arch::Arch, instance::Instance}; /// to populate the module with the executable code and data, which is then /// used to create instances of the module. pub struct Module { - /// The resource ID. - id: u64, /// The module ID. Provided by the ring spawner and used /// to refer to the module during module loading. module_id: Id<{ IdType::Module }>, - /// The list of instances spawned from this module. - pub(super) instances: Vec>>>, /// The module's address space mapper handle. /// /// Only uninitialized if the module is in the process of being freed. @@ -43,26 +35,16 @@ pub struct Module { impl Module { /// Creates a new module. - pub fn new(module_id: Id<{ IdType::Module }>) -> Result>, MapError> { - let id = crate::id::allocate(); - + pub fn new(module_id: Id<{ IdType::Module }>) -> Result, MapError> { let mapper = AddressSpace::::new_user_space_empty().ok_or(MapError::OutOfMemory)?; - let r = Arc::new(ReentrantMutex::new(Self { - id, - module_id, - instances: Vec::new(), - mapper, - entry_points: Vec::new(), - })); - - Ok(r) - } - - /// Returns the instance ID. - #[must_use] - pub fn id(&self) -> u64 { - self.id + crate::tab::get() + .add(Self { + module_id, + mapper, + entry_points: Vec::new(), + }) + .ok_or(MapError::OutOfMemory) } /// Returns the module ID. @@ -71,11 +53,6 @@ impl Module { &self.module_id } - /// Returns a list of weak handles to instances spawned from this module. - pub fn instances(&self) -> &[Weak>>] { - &self.instances - } - /// Returns the module's user address space mapper handle. /// /// **All mappings created with this handle are shared between all instances of this module.** @@ -97,18 +74,7 @@ impl Module { impl Drop for Module { fn drop(&mut self) { - // NOTE(qix-): Do not call assoiated methods on `self` within this method. - - // Make sure all instances have been properly destroyed prior - // to dropping the module. - for instance in &self.instances { - if let Some(instance) = instance.upgrade() { - panic!( - "module dropped with active instance(s); first found is {:?}", - instance.lock().id() - ); - } - } + // NOTE(qix-): Do not call associated methods on `self` within this method. // Statically ensure that handles do not have drop semantics. // Otherwise, the following `unsafe` block would be unsound. diff --git a/oro-kernel/src/port.rs b/oro-kernel/src/port.rs deleted file mode 100644 index de3fe470..00000000 --- a/oro-kernel/src/port.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Implements Oro ports in the kernel. - -use oro_id::{Id, IdType}; - -/// A singular port. -/// -/// Ports are unidirectional communication channels between -/// [`crate::instance::Instance`]s. They are implemented -/// as ring buffers of slotted messages of fixed size, the size -/// being determined by metadata associated with the port's type. -/// -/// Ports are the primary means of communication not only between -/// module instances, but also between the built-in kernel modules -/// provided to the system on the root [`crate::ring::Ring`]. -/// -/// Ports are exposed to userspace applications at specific addresses -/// in the process address space, and are governed by a system of -/// page swaps, faults, and other mechanisms to ensure ordered delivery. -/// -/// Ports are wired up prior to the module instance being spawned, such -/// that instance code can assume that the ports are already available -/// and guaranteed to function. It's the kernel's responsibility to -/// wire up the appropriate ports to the at the appropriate addresses -/// in the address space. -/// -/// # Synchronous Ports -/// By default, ports are synchronous. A write to a full buffer on the -/// producer side will page fault and schedule the thread for execution -/// once the slot being written to has been consumed. Likewise, an empty -/// buffer on the consumer side will page fault and schedule the thread -/// for execution once the slot being read from has been produced. -/// -/// This is the default behavior, and under certain circumstances, -/// the only behavior (e.g. the user has prohibited "asynchronous upgrades" -/// on a port). -/// -/// # Asynchronous Ports -/// Ports are able to read and write to the buffer cursors in order to -/// manage the buffer themselves. If allowed, a sequence of reads and -/// writes will automatically upgrade the port to an asynchronous port -/// for **both the producer and consumer** (meaning that both sides -/// must behave in the same way in order for the kernel to recognize -/// the upgrade). -/// -/// In this mode, page faulting is disabled, and the pages are mapped -/// with the appropriate permissions to allow the producer and consumer -/// to read and write to the buffer directly, including ahead of time. -/// -/// This mode is inherently unsafe, as it introduces the possibility -/// of race conditions and potential data corruption. Thus, it is -/// possible for the user to prohibit asynchronous upgrades on a port -/// in the event one is behaving incorrectly. -/// -/// Asynchronous port operation is useful for high-performance, low-latency -/// communication in cases where pre-empting the thread is not desirable, -/// or whereby the overhead introduced by the page faulting system is -/// too high. -/// -/// # Port Types -/// Ports are typed, and the type of the port determines if a producer -/// and consumer can be connected. The kernel will enforce this type -/// checking at the time of wiring up the ports, and will refuse to -/// begin execution of a module instance if not. -/// -/// Port types also define their message size, along with other metadata -/// about the port. There are no "standard" port types; they are governed -/// entirely by the ecosystem. For example, the kernel provides several -/// built-in module instances and describes the ports they export and -/// import at the port level. -/// -/// It is only imperative that the module loading system provides the -/// kernel with the size of messages for each port type; nothing else -/// is inspected beyond that. -/// -/// # Thread Ownership -/// Modules are allowed to spawn threads, and those threads are allowed -/// to read and write to ports. However, ports are owned by a single -/// thread, and only that thread is allowed to read and write to the -/// port at a given time. Attempts to read/write to a port from a thread -/// that does not own it will result in a page fault. -/// -/// Ownership of a port may be transferred to another thread, however -/// this is a somewhat expensive operation and should be done sparingly. -pub struct Port { - /// The resource ID. - id: u64, - /// The type ID of the port. - type_id: Id<{ IdType::PortType }>, - /// Gets the length of the port's message. - slot_size: usize, -} - -impl Port { - /// Returns the port's ID. - #[must_use] - pub fn id(&self) -> u64 { - self.id - } - - /// Returns the port's type ID. - #[must_use] - pub fn type_id(&self) -> &Id<{ IdType::PortType }> { - &self.type_id - } - - /// Returns the port's slot size. - #[must_use] - pub fn slot_size(&self) -> usize { - self.slot_size - } -} diff --git a/oro-kernel/src/registry.rs b/oro-kernel/src/registry.rs deleted file mode 100644 index 7d8af3db..00000000 --- a/oro-kernel/src/registry.rs +++ /dev/null @@ -1,188 +0,0 @@ -//! Oro kernel object registry implementation. -#![expect(unused_imports)] - -use core::{ - mem::MaybeUninit, - sync::atomic::{ - AtomicBool, - Ordering::{Acquire, Release}, - }, -}; - -use oro_mem::alloc::{ - collections::btree_map::BTreeMap, - sync::{Arc, Weak}, -}; -use oro_sync::{Lock, ReentrantMutex}; -use oro_sysabi::{ - key, - syscall::{Error, Opcode, Result}, -}; - -use crate::{ - arch::Arch, - interface::{Interface, InterfaceResponse, SystemCallRequest, SystemCallResponse}, - tab::Tab, - table::Table, - thread::Thread, -}; - -/// A system-wide "master" registry. -/// -/// Holds all interface handles in the system, -/// each identified by a unique ID that monotonically -/// increases upon insertion via [`crate::id::allocate()`]. -/// -/// The lower level entity types (rings, instances, etc.) hold -/// a view into this registry, caching interfaces as needed -/// so as to reduce pressure on the registry's locks. -pub struct RootRegistry { - /// A map of all registered interfaces. - interface_map: Table>>, -} - -impl RootRegistry { - /// Creates a new, fully empty registry. - #[must_use] - pub fn new_empty() -> Self { - Self { - interface_map: Table::new(), - } - } -} - -/// Implements access to a [`RootRegistry`] or a [`RegistryView`] -/// (or some wrapper thereof). -pub trait Registry: Send { - /// Inserts an interface into the registry and returns its globally unique ID. - /// - /// The interface is guaranteed to be unique in the registry, and is registered - /// globally. - fn register_interface(&mut self, interface: Arc>) -> u64; - - /// Looks up an interface by its globally unique ID. If this is a view, - /// it may cache the interface for future lookups. - fn lookup(&mut self, interface_id: u64) -> Option>>; - - /// Handles a system call request to the registry. - fn dispatch( - &mut self, - thread: &Tab>, - request: &SystemCallRequest, - ) -> InterfaceResponse { - let error = match request.opcode { - const { Opcode::Get as u64 } => { - if (request.arg1 & oro_sysabi::id::mask::KERNEL_ID) == 0 { - if let Some(res) = crate::iface::kernel::try_dispatch_get::( - thread, - request.arg1, - request.arg2, - request.arg3, - ) { - return res; - } - - Error::BadInterface - } else { - match self.lookup(request.arg1) { - Some(interface) => { - return interface.get(thread, request.arg2, request.arg3); - } - None => Error::BadInterface, - } - } - } - const { Opcode::Set as u64 } => { - if (request.arg1 & oro_sysabi::id::mask::KERNEL_ID) == 0 { - if let Some(res) = crate::iface::kernel::try_dispatch_set::( - thread, - request.arg1, - request.arg2, - request.arg3, - request.arg4, - ) { - return res; - } - - Error::BadInterface - } else { - match self.lookup(request.arg1) { - Some(interface) => { - return interface.set(thread, request.arg2, request.arg3, request.arg4); - } - None => Error::BadInterface, - } - } - } - _ => Error::BadOpcode, - }; - - InterfaceResponse::Immediate(SystemCallResponse { error, ret: 0 }) - } -} - -impl Registry for RootRegistry { - #[inline] - fn register_interface(&mut self, interface: Arc>) -> u64 { - self.interface_map.insert_unique(interface) - } - - #[inline] - fn lookup(&mut self, interface_id: u64) -> Option>> { - self.interface_map.get(interface_id).cloned() - } -} - -/// A scoped / cached view into a parent [`Registry`]. -/// -/// Accesses to the registry through this type are cached, -/// reducing contention on the parent registry's locks. -pub struct RegistryView> { - /// The parent registry from which to fetch interfaces. - parent: Arc>, - /// A cache of interfaces. - // TODO(qix-): Use an LFU? - cache: Table>>, -} - -impl> RegistryView { - /// Creates a new registry view into the given parent registry. - pub fn new(parent: Arc>) -> Self { - Self { - parent, - cache: Table::new(), - } - } -} - -impl> Registry for RegistryView { - fn register_interface(&mut self, interface: Arc>) -> u64 { - let weak = Arc::downgrade(&interface); - let id = self.parent.lock().register_interface(interface); - // SAFETY: We can assume that the interface has not been inserted before, since - // SAFETY: `register_interface` guarantees that the interface is unique. - unsafe { - self.cache.insert_unique_unchecked(id, weak); - } - id - } - - fn lookup(&mut self, interface_id: u64) -> Option>> { - self.cache - .get(interface_id) - .and_then(Weak::upgrade) - .or_else(|| { - self.parent - .lock() - .lookup(interface_id) - .inspect(|interface| { - let weak = Arc::downgrade(interface); - // SAFETY: We know it doesn't exist in this view because we just checked - // SAFETY: and we currently have an exclusive (mutable) reference to `self`. - unsafe { - self.cache.insert_unique_unchecked(interface_id, weak); - } - }) - }) - } -} diff --git a/oro-kernel/src/ring.rs b/oro-kernel/src/ring.rs index fb480d02..5cf2e714 100644 --- a/oro-kernel/src/ring.rs +++ b/oro-kernel/src/ring.rs @@ -1,19 +1,13 @@ //! Implements Oro rings in the kernel. use oro_mem::{ - alloc::{ - sync::{Arc, Weak}, - vec::Vec, - }, + alloc::vec::Vec, mapper::{AddressSegment, AddressSpace as _, MapError}, }; -use oro_sync::{Lock, ReentrantMutex}; use crate::{ - AddressSpace, Kernel, UserHandle, - arch::Arch, - instance::Instance, - registry::{RegistryView, RootRegistry}, + AddressSpace, Kernel, UserHandle, arch::Arch, instance::Instance, interface::RingInterface, + tab::Tab, }; /// A singular ring. @@ -27,7 +21,7 @@ use crate::{ /// However, module instances on a ring cannot see 'sibling' or parent /// rings, or anything on them, under any circumstance. This is enforced /// by the kernel. The resources they have access to are limited to those -/// explicitly granted to them by the parent ring via [`crate::port::Port`]s. +/// explicitly granted to them by the parent ring via [`crate::interface::Interface`]s. /// /// Rings have exactly one parent ring, and can have any number of child /// rings. The root ring is the only ring that has no parent ring, and is @@ -35,49 +29,42 @@ use crate::{ /// the root ring are effectively at the highest privilege level of the /// system, and can interact with the kernel directly. Child rings may /// only do so if one of the root ring's module instances has granted -/// them such access via a port. +/// them such access via a port or interface. #[non_exhaustive] pub struct Ring { - /// The resource ID. - id: u64, /// The parent ring handle, or `None` if this is the root ring. - parent: Option>>>, + parent: Option>>, /// The module [`Instance`]s on the ring. - instances: Vec>>>, + instances: Vec>>, /// The ring's base mapper handle. - mapper: UserHandle, + mapper: UserHandle, /// The ring's child rings. - children: Vec>>>, - /// The ring's registry. - registry: Arc>>>, + children: Vec>>, + /// The interfaces exposed to the ring. + interfaces: Vec>>, } impl Ring { /// Creates a new ring. - pub fn new( - parent: &Arc>>, - ) -> Result>, MapError> { - let id = crate::id::allocate(); - + pub fn new(parent: &Tab>) -> Result, MapError> { let mapper = AddressSpace::::new_user_space(&Kernel::::get().mapper) .ok_or(MapError::OutOfMemory)?; AddressSpace::::sysabi().provision_as_shared(&mapper)?; - let r = Arc::new(ReentrantMutex::new(Self { - id, - parent: Some(Arc::downgrade(parent)), - instances: Vec::new(), - mapper, - children: Vec::new(), - registry: Arc::new(ReentrantMutex::new(RegistryView::new( - crate::Kernel::::get().state().registry().clone(), - ))), - })); + let tab = crate::tab::get() + .add(Self { + parent: Some(parent.clone()), + instances: Vec::new(), + mapper, + children: Vec::new(), + interfaces: Vec::new(), + }) + .ok_or(MapError::OutOfMemory)?; - parent.lock().children.push(r.clone()); + parent.with_mut(|parent| parent.children.push(tab.clone())); - Ok(r) + Ok(tab) } /// Creates a new root ring. @@ -90,9 +77,7 @@ impl Ring { /// /// Caller **must** push the ring onto the kernel state's `rings` list itself; /// this method **will not** do it for you. - pub(crate) unsafe fn new_root( - registry: Arc>>, - ) -> Result>, MapError> { + pub(crate) unsafe fn new_root() -> Result, MapError> { // NOTE(qix-): This method CANNOT call `Kernel::::get()` because // NOTE(qix-): core-local kernels are not guaranteed to be initialized // NOTE(qix-): at this point in the kernel's lifetime. @@ -109,36 +94,23 @@ impl Ring { AddressSpace::::sysabi().provision_as_shared(&mapper)?; - let r = Arc::new(ReentrantMutex::new(Self { - id: 0, - parent: None, - instances: Vec::new(), - mapper, - children: Vec::new(), - registry: Arc::new(ReentrantMutex::new(RegistryView::new(registry))), - })); - - Ok(r) - } - - /// Returns the ring's ID. - #[must_use] - pub fn id(&self) -> u64 { - self.id + crate::tab::get() + .add(Self { + parent: None, + instances: Vec::new(), + mapper, + children: Vec::new(), + interfaces: Vec::new(), + }) + .ok_or(MapError::OutOfMemory) } /// Returns the ring's parent ring weak handle. /// /// If the ring is the root ring, this function will return `None`. #[must_use] - pub fn parent(&self) -> Option>>> { - self.parent.clone() - } - - /// Returns a slice of instances on the ring. - #[must_use] - pub fn instances(&self) -> &[Arc>>] { - &self.instances + pub fn parent(&self) -> Option<&Tab>> { + self.parent.as_ref() } /// Returns a reference to the ring's mapper. @@ -147,15 +119,27 @@ impl Ring { &self.mapper } + /// Returns a slice of instances on the ring. + #[must_use] + pub fn instances(&self) -> &[Tab>] { + &self.instances + } + /// Returns a mutable reference to the instances vector. #[must_use] - pub fn instances_mut(&mut self) -> &mut Vec>>> { + pub fn instances_mut(&mut self) -> &mut Vec>> { &mut self.instances } - /// Returns the ring's registry handle. + /// Returns a slice of interfaces exposed to the ring. + #[must_use] + pub fn interfaces(&self) -> &[Tab>] { + &self.interfaces + } + + /// Returns a mutable reference to the interfaces vector. #[must_use] - pub fn registry(&self) -> &Arc>>> { - &self.registry + pub fn interfaces_mut(&mut self) -> &mut Vec>> { + &mut self.interfaces } } diff --git a/oro-kernel/src/scheduler.rs b/oro-kernel/src/scheduler.rs index e7f8ac50..f52bf6f3 100644 --- a/oro-kernel/src/scheduler.rs +++ b/oro-kernel/src/scheduler.rs @@ -5,8 +5,7 @@ use oro_sync::Lock; use crate::{ Kernel, arch::{Arch, CoreHandle}, - interface::{InFlightSystemCall, InterfaceResponse, SystemCallRequest, SystemCallResponse}, - registry::Registry, + syscall::{InFlightSystemCall, InterfaceResponse, SystemCallRequest, SystemCallResponse}, tab::Tab, thread::{RunState, ScheduleAction, Thread}, }; @@ -118,7 +117,7 @@ impl Scheduler { /// is running. #[must_use] pub unsafe fn event_idle(&mut self) -> Switch { - let coming_from_user = self.current.as_ref().map(|t| t.with(|t| t.id())); + let coming_from_user = self.current.as_ref().map(|t| t.id()); let switch = Switch::from_schedule_action(self.pick_user_thread(), coming_from_user); self.kernel.handle().schedule_timer(1000); switch @@ -149,7 +148,7 @@ impl Scheduler { /// is running. #[must_use] pub unsafe fn event_timer_expired(&mut self) -> Switch { - let coming_from_user = self.current.as_ref().map(|t| t.with(|t| t.id())); + let coming_from_user = self.current.as_ref().map(|t| t.id()); let switch = Switch::from_schedule_action(self.pick_user_thread(), coming_from_user); self.kernel.handle().schedule_timer(1000); switch @@ -194,21 +193,7 @@ impl Scheduler { #[must_use] pub unsafe fn event_system_call(&mut self, request: &SystemCallRequest) -> Switch { let coming_from_user = if let Some(thread) = self.current.take() { - let response = { - // TODO(qix-): use a deeper cache than the ring's - if let Some(ring) = thread.with(|t| t.instance()).lock().ring().upgrade() { - let ring_lock = ring.lock(); - let registry = ring_lock.registry(); - let mut registry_lock = registry.lock(); - registry_lock.dispatch(&thread, request) - } else { - // The instance is gone; we can't do anything. - // This is a critical error. - panic!( - "instance is still running but has no ring; kernel is in an invalid state" - ); - } - }; + let response = crate::syscall::dispatch(&thread, request); // If the thread was stopped or terminated by the syscall, we need to // handle it specially. @@ -219,23 +204,14 @@ impl Scheduler { } (RunState::Stopped, InterfaceResponse::Immediate(response)) => { let (sub, handle) = InFlightSystemCall::new(); - let id = thread.with_mut(|t| { - t.await_system_call_response(self.kernel.id(), handle); - t.id() - }); + thread.with_mut(|t| t.await_system_call_response(self.kernel.id(), handle)); sub.submit(response); - Some(id) - } - (RunState::Terminated, _) => { - let id = thread.with(|t| t.id()); - Some(id) + Some(thread.id()) } + (RunState::Terminated, _) => Some(thread.id()), (RunState::Running | RunState::Stopped, InterfaceResponse::Pending(handle)) => { - let id = thread.with_mut(|t| { - t.await_system_call_response(self.kernel.id(), handle); - t.id() - }); - Some(id) + thread.with_mut(|t| t.await_system_call_response(self.kernel.id(), handle)); + Some(thread.id()) } } } else { @@ -294,7 +270,7 @@ impl Switch { match (action, coming_from_user) { (Some((thread, ScheduleAction::Resume)), None) => Switch::KernelToUser(thread, None), (Some((thread, ScheduleAction::Resume)), Some(old_id)) => { - if thread.with(|t| t.id()) == old_id { + if thread.id() == old_id { Switch::UserResume(thread, None) } else { Switch::UserToUser(thread, None) @@ -304,7 +280,7 @@ impl Switch { Switch::KernelToUser(thread, Some(syscall_res)) } (Some((thread, ScheduleAction::SystemCall(syscall_res))), Some(old_id)) => { - if thread.with(|t| t.id()) == old_id { + if thread.id() == old_id { Switch::UserResume(thread, Some(syscall_res)) } else { Switch::UserToUser(thread, Some(syscall_res)) diff --git a/oro-kernel/src/syscall.rs b/oro-kernel/src/syscall.rs new file mode 100644 index 00000000..4dc29b63 --- /dev/null +++ b/oro-kernel/src/syscall.rs @@ -0,0 +1,268 @@ +//! Dispatch point for servicing system calls. + +use core::{ + cell::UnsafeCell, + mem::MaybeUninit, + sync::atomic::{ + AtomicU8, + Ordering::{Acquire, Relaxed, Release}, + }, +}; + +use oro_mem::alloc::sync::Arc; +use oro_sysabi::syscall::{Error, Opcode}; + +use crate::{ + arch::Arch, + interface::{Interface, RingInterface}, + tab::Tab, + thread::Thread, +}; + +/// Dispatches a syscall on behalf of a thread. +#[must_use] +pub fn dispatch( + thread: &Tab>, + request: &SystemCallRequest, +) -> InterfaceResponse { + let error = match request.opcode { + const { Opcode::Get as u64 } => { + if (request.arg1 & oro_sysabi::id::mask::KERNEL_ID) == 0 { + if let Some(res) = crate::iface::kernel::try_dispatch_get::( + thread, + request.arg1, + request.arg2, + request.arg3, + ) { + return res; + } + + Error::BadInterface + } else { + match crate::tab::get().lookup::>(request.arg1) { + Some(interface) => { + return interface.with(|i| i.get(thread, request.arg2, request.arg3)); + } + None => Error::BadInterface, + } + } + } + const { Opcode::Set as u64 } => { + if (request.arg1 & oro_sysabi::id::mask::KERNEL_ID) == 0 { + if let Some(res) = crate::iface::kernel::try_dispatch_set::( + thread, + request.arg1, + request.arg2, + request.arg3, + request.arg4, + ) { + return res; + } + + Error::BadInterface + } else { + match crate::tab::get().lookup::>(request.arg1) { + Some(interface) => { + return interface + .with(|i| i.set(thread, request.arg2, request.arg3, request.arg4)); + } + None => Error::BadInterface, + } + } + } + _ => Error::BadOpcode, + }; + + InterfaceResponse::Immediate(SystemCallResponse { error, ret: 0 }) +} + +/// Response from an interface after handling a system call. +/// +/// When performing an [`Interface`] syscall operation, the interface can either +/// respond immediately, or defer the response to a later time. In the latter case, +/// a pollable handle is returned. +pub enum InterfaceResponse { + /// The interface has handled the request and the response is ready. + Immediate(SystemCallResponse), + /// The interface has received the request, but the response is not ready yet. + Pending(InFlightSystemCallHandle), +} + +/// The producer side of an in-flight system call. +/// +/// Interfaces should hold onto this handle when they defer a system call response, +/// allowing to submit the response at a later time. +#[repr(transparent)] +pub struct InFlightSystemCall(Arc); + +/// Inner shared state for an in-flight system call. +struct InFlightSystemCallInner { + /// The response is ready. Used as an atomic rather than an `Option`. + state: AtomicU8, + /// The response data; only valid if `state` is [`InFlightState::Ready`]. + response: UnsafeCell>, +} + +// SAFETY: We strictly control access to the inner state and control exactly +// SAFETY: how it is used, and can guarantee that it is safe to share across +// SAFETY: threads. Any misbehavior is a bug. +unsafe impl Sync for InFlightSystemCallInner {} +// SAFETY: The inner state is valid across threads, despite using an unsafe cell. +unsafe impl Send for InFlightSystemCallInner {} + +/// Indicates the state of the in-flight system call. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum InFlightState { + /// Pending + Pending, + /// The response is ready. + Ready, + /// The caller has canceled the system call. + CallerCanceled, + /// The interface has canceled the system call. + InterfaceCanceled, + /// The interface serviced the response, and the caller has + /// already taken the response. This is considered an error. + Finished, +} + +/// The caller side of an in-flight system call. +pub struct InFlightSystemCallHandle(Arc); + +impl InFlightSystemCall { + /// Creates a new in-flight system call interface/caller pair. + /// + /// The interface should call this, hold on to the first element + /// (the interface side), and return the second element (the caller side) + /// via [`InterfaceResponse::Pending`]. + /// + /// Once the interface has a response, it can submit it via [`Self::submit`], + /// which consumes this handle and notifies the caller. + #[must_use] + pub fn new() -> (Self, InFlightSystemCallHandle) { + let inner = Arc::new(InFlightSystemCallInner { + state: AtomicU8::new(InFlightState::Pending as u8), + response: UnsafeCell::new(MaybeUninit::uninit()), + }); + + (Self(inner.clone()), InFlightSystemCallHandle(inner)) + } + + /// Checks if the caller canceled the system call. + /// + /// There's no need to check this before submitting a response; however, + /// if the caller is doing something long-running, it may be useful to + /// check, as if this returns `true`, the response will be dropped immediately. + #[inline] + #[must_use] + pub fn canceled(&self) -> bool { + self.0.state.load(Relaxed) == (InFlightState::CallerCanceled as u8) + } + + /// Submits a response to the in-flight system call, consuming the + /// interface side of the handle. + /// + /// **Note:** there is no guarantee that the response will be taken by the + /// caller; if the caller canceled the system call, the response will be + /// dropped. Further, the caller may cancel the system call after this + /// method is called, but before the response is taken. + /// + /// If the interface's processing of this sytem call request is long-winded, + /// and side effects are not a concern, it may be useful to check if the + /// system call was canceled before submitting the response via [`Self::canceled`]. + pub fn submit(self, response: SystemCallResponse) { + // SAFETY: We are the only ones that can write to the response, + // SAFETY: and it can only happen here, once. + unsafe { self.0.response.get().write(MaybeUninit::new(response)) }; + // NOTE(qix-): Even if the caller canceled the system call, this is + // NOTE(qix-): innocuous; it's going to get dropped anyway, and we + // NOTE(qix-): save a branch. + self.0.state.store(InFlightState::Ready as u8, Release); + } +} + +impl Drop for InFlightSystemCall { + fn drop(&mut self) { + self.0 + .state + .compare_exchange( + InFlightState::Pending as u8, + InFlightState::InterfaceCanceled as u8, + Relaxed, + Relaxed, + ) + .ok(); + } +} + +impl InFlightSystemCallHandle { + /// Get the current state of the in-flight system call. + #[inline] + #[must_use] + pub fn state(&self) -> InFlightState { + // SAFETY: We control the value entirely; we can transmute it back. + unsafe { ::core::mem::transmute::(self.0.state.load(Acquire)) } + } + + /// Try to take the response from the in-flight system call. + /// + /// - If the response is ready, it is returned as `Ok(Some(response))`. + /// - If the response is not ready, `Ok(None)` is returned. + /// - If the system call was canceled by the interface, `Err(InFlightState::InterfaceCanceled)` + /// is returned. + /// - Attempts to take the response after it has been taken by the caller will return + /// `Err(InFlightState::Finished)`. + #[inline] + pub fn try_take_response(&self) -> Result, InFlightState> { + match self.state() { + InFlightState::Pending => Ok(None), + InFlightState::CallerCanceled => unreachable!(), + InFlightState::Ready => { + // SAFETY: We are the only ones that can write to the response, + // SAFETY: and it can only happen once. + self.0.state.store(InFlightState::Finished as u8, Release); + Ok(Some(unsafe { self.0.response.get().read().assume_init() })) + } + status @ (InFlightState::Finished | InFlightState::InterfaceCanceled) => Err(status), + } + } +} + +impl Drop for InFlightSystemCallHandle { + fn drop(&mut self) { + self.0 + .state + .compare_exchange( + InFlightState::Pending as u8, + InFlightState::CallerCanceled as u8, + Relaxed, + Relaxed, + ) + .ok(); + } +} + +/// System call request data. +#[derive(Debug, Clone)] +pub struct SystemCallRequest { + /// The opcode. + pub opcode: u64, + /// The first argument. + pub arg1: u64, + /// The second argument. + pub arg2: u64, + /// The third argument. + pub arg3: u64, + /// The fourth argument. + pub arg4: u64, +} + +/// System call response data. +#[derive(Debug, Clone, Copy)] +pub struct SystemCallResponse { + /// The error code. + pub error: oro_sysabi::syscall::Error, + /// The return value. + pub ret: u64, +} diff --git a/oro-kernel/src/tab.rs b/oro-kernel/src/tab.rs index 4f0e428e..557891d1 100644 --- a/oro-kernel/src/tab.rs +++ b/oro-kernel/src/tab.rs @@ -383,10 +383,6 @@ trait Tabbed { const TY: TabType; } -impl Tabbed for crate::ring::Ring { - const TY: TabType = TabType::Ring; -} - /// A subtable, holding 512 entries to `AtomicPtr`. struct SubTable { /// The table. @@ -690,26 +686,6 @@ impl Drop for Tab { } } -/// The type of value held in the tab slot. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[repr(u8)] -pub enum TabType { - /// The tab slot is free. - Free = 0, - /// A [`crate::ring::Ring`]. - Ring, - /// An [`crate::instance::Instance`]. - Instance, - /// A [`crate::thread::Thread`]. - Thread, - /// A [`crate::port::Port`]. - Port, - /// An [`crate::interface::Interface`]. - Interface, - /// A [`crate::module::Module`]. - Module, -} - /// A versioned slot within which to store a [`Tab`]'s data. #[derive(Default)] #[repr(C)] @@ -1099,6 +1075,24 @@ impl Slot { } } +/// The type of value held in the tab slot. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(u8)] +pub enum TabType { + /// The tab slot is free. + Free = 0, + /// A [`crate::ring::Ring`]. + Ring, + /// An [`crate::instance::Instance`]. + Instance, + /// A [`crate::thread::Thread`]. + Thread, + /// A [`crate::interface::RingInterface`]. + RingInterface, + /// A [`crate::module::Module`]. + Module, +} + impl Tabbed for crate::thread::Thread { const TY: TabType = TabType::Thread; } @@ -1106,3 +1100,15 @@ impl Tabbed for crate::thread::Thread { impl Tabbed for crate::instance::Instance { const TY: TabType = TabType::Instance; } + +impl Tabbed for crate::module::Module { + const TY: TabType = TabType::Module; +} + +impl Tabbed for crate::ring::Ring { + const TY: TabType = TabType::Ring; +} + +impl Tabbed for crate::interface::RingInterface { + const TY: TabType = TabType::RingInterface; +} diff --git a/oro-kernel/src/table.rs b/oro-kernel/src/table.rs index 8c05c1fc..a60d7fbd 100644 --- a/oro-kernel/src/table.rs +++ b/oro-kernel/src/table.rs @@ -26,21 +26,6 @@ impl Table { Self(HashMap::default()) } - /// Inserts a value into the table, allocating it a unique ID using the - /// system's monotonic ID allocator ([`crate::id::allocate`]). - /// - /// Lock-free. - pub fn insert_unique(&mut self, value: T) -> u64 { - let id = crate::id::allocate(); - // SAFETY: This operation is safe if a key does not exist in the map. - // SAFETY: Since `id::allocate()` is guaranteed to be unique, this - // SAFETY: allows us to avoid a lookup. - unsafe { - self.0.insert_unique_unchecked(id, value); - } - id - } - /// Inserts a value into the table with a specific ID. /// /// Safely checks for collisions and drops the old value if one exists. diff --git a/oro-kernel/src/thread.rs b/oro-kernel/src/thread.rs index f5f6241e..d2a79dae 100644 --- a/oro-kernel/src/thread.rs +++ b/oro-kernel/src/thread.rs @@ -11,19 +11,19 @@ use oro_macro::AsU64; use oro_mem::{ - alloc::sync::Arc, global_alloc::GlobalPfa, mapper::{AddressSegment, AddressSpace as _, MapError, UnmapError}, pfa::Alloc, }; -use oro_sync::{Lock, ReentrantMutex}; +use oro_sync::Lock; use oro_sysabi::{key, syscall::Error as SysError}; use crate::{ AddressSpace, Kernel, UserHandle, arch::{Arch, ThreadHandle}, instance::Instance, - interface::{InFlightState, InFlightSystemCall, InFlightSystemCallHandle, SystemCallResponse}, + ring::Ring, + syscall::{InFlightState, InFlightSystemCall, InFlightSystemCallHandle, SystemCallResponse}, tab::Tab, }; @@ -67,10 +67,11 @@ enum State { /// other OSes, are not nested (i.e. a thread does not /// have a parent thread). pub struct Thread { - /// The resource ID. + /// The tab's ID. Stored here for internal stuff; + /// exposed via [`Tab::id()`]. id: u64, /// The module instance to which this thread belongs. - instance: Arc>>, + instance: Tab>, /// Architecture-specific thread state. handle: A::ThreadHandle, /// The thread's state (during running). @@ -87,16 +88,15 @@ impl Thread { /// Creates a new thread in the given module instance. #[expect(clippy::missing_panics_doc)] pub fn new( - instance: &Arc>>, + instance: &Tab>, entry_point: usize, ) -> Result>, MapError> { - let id = crate::id::allocate(); - // Pre-calculate the stack pointer. // TODO(qix-): If/when we support larger page sizes, this will need to be adjusted. let stack_ptr = AddressSpace::::user_thread_stack().range().1 & !0xFFF; - let mapper = AddressSpace::::duplicate_user_space_shallow(instance.lock().mapper()) + let mapper = instance + .with(|instance| AddressSpace::::duplicate_user_space_shallow(instance.mapper())) .ok_or(MapError::OutOfMemory)?; let handle = A::ThreadHandle::new(mapper, stack_ptr, entry_point)?; @@ -179,7 +179,7 @@ impl Thread { // We do this before we create the tab just in case we're OOM // and need to have the thread clean itself up. let this = Self { - id, + id: 0, // will be set later instance: instance.clone(), handle, state: State::default(), @@ -189,7 +189,9 @@ impl Thread { let tab = crate::tab::get().add(this).ok_or(MapError::OutOfMemory)?; - instance.lock().threads.insert(id, tab.clone()); + tab.with_mut(|t| t.id = tab.id()); + + instance.with_mut(|instance| instance.threads.insert(tab.id(), tab.clone())); Kernel::::get() .state() @@ -200,17 +202,10 @@ impl Thread { Ok(tab) } - /// Returns the thread's ID. - #[must_use] - #[inline] - pub fn id(&self) -> u64 { - self.id - } - /// Returns module instance handle to which this thread belongs. #[inline] - pub fn instance(&self) -> Arc>> { - self.instance.clone() + pub fn instance(&self) -> &Tab> { + &self.instance } /// Returns the thread's address space handle. @@ -289,6 +284,13 @@ impl Thread { } } + /// Returns the thread's owning ring. + /// + /// **Note:** This will temporarily lock the thread's instance. + pub fn ring(&self) -> Tab> { + self.instance.with(|instance| instance.ring().clone()) + } + /// Attempts to pause the thread on the given core. /// /// The thread must already be running on the given core, @@ -398,13 +400,14 @@ impl Thread { self.state = State::Unallocated; // Remove the thread from the instance's thread table. - self.instance.lock().threads.remove(self.id); + self.instance + .with_mut(|instance| instance.threads.remove(self.id)); // Remove the thread from the global thread table. // TODO(qix-): This is horribly inefficient but it's the best I can do for now // TODO(qix-): until a better data structure for the schedulers is implemented. let mut threads = Kernel::::get().state().threads().lock(); - threads.retain(|t| t.with(|t| t.id() != self.id)); + threads.retain(|t| t.id() != self.id); } } @@ -464,6 +467,10 @@ pub enum ScheduleAction { impl Drop for Thread { fn drop(&mut self) { + // SAFETY(qix-): Do NOT rely on `self.id` being valid here. + // SAFETY(qix-): There's a chance the thread is dropped during construction, + // SAFETY(qix-): before it's registered with the tab registry. + let old_state = core::mem::replace(&mut self.state, State::Unallocated); // Sanity check; make sure the thread is not running on any scheduler,