diff --git a/src/main.rs b/src/main.rs index 1af39cb548..b9d15673df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,6 +49,7 @@ #![feature(naked_functions)] #![feature(offset_of)] #![feature(sync_unsafe_cell)] +#![feature(variant_count)] #![no_std] #![no_main] diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs index 97cf9a9962..0b2742a12c 100644 --- a/src/scheme/acpi.rs +++ b/src/scheme/acpi.rs @@ -9,7 +9,6 @@ use spin::{Mutex, Once, RwLock}; use crate::acpi::{RXSDT_ENUM, RxsdtEnum}; use crate::event; -use crate::scheme::SchemeId; use crate::sync::WaitCondition; use crate::syscall::data::Stat; @@ -23,7 +22,7 @@ use crate::syscall::flag::{ use crate::syscall::error::{Error, Result}; use crate::syscall::usercopy::{UserSliceRo, UserSliceWo}; -use super::{KernelScheme, CallerCtx, OpenResult}; +use super::{GlobalSchemes, KernelScheme, CallerCtx, OpenResult}; /// A scheme used to access the RSDT or XSDT, which is needed for e.g. `acpid` to function. pub struct AcpiScheme; @@ -50,21 +49,15 @@ const TOPLEVEL_CONTENTS: &[u8] = b"rxsdt\nkstop\n"; static KSTOP_WAITCOND: WaitCondition = WaitCondition::new(); static KSTOP_FLAG: Mutex = Mutex::new(false); -static SCHEME_ID: Once = Once::new(); - pub fn register_kstop() -> bool { *KSTOP_FLAG.lock() = true; let mut waiters_awoken = KSTOP_WAITCOND.notify(); - if let Some(&acpi_scheme) = SCHEME_ID.get() { - let handles = HANDLES.read(); + let handles = HANDLES.read(); - for (&fd, _) in handles.iter().filter(|(_, handle)| handle.kind == HandleKind::ShutdownPipe) { - event::trigger(acpi_scheme, fd, EVENT_READ); - waiters_awoken += 1; - } - } else { - log::error!("Calling register_kstop before kernel ACPI scheme was initialized"); + for (&fd, _) in handles.iter().filter(|(_, handle)| handle.kind == HandleKind::ShutdownPipe) { + event::trigger(GlobalSchemes::Acpi.scheme_id(), fd, EVENT_READ); + waiters_awoken += 1; } if waiters_awoken == 0 { @@ -78,13 +71,12 @@ pub fn register_kstop() -> bool { } impl AcpiScheme { - pub fn init(id: SchemeId) { + pub fn init() { // NOTE: This __must__ be called from the main kernel context, while initializing all // schemes. If it is called by any other context, then all ACPI data will probably not even // be mapped. let mut data_init = false; - let mut id_init = false; DATA.call_once(|| { data_init = true; @@ -100,13 +92,8 @@ impl AcpiScheme { Box::from(table) }); - SCHEME_ID.call_once(|| { - id_init = true; - id - }); - - if !data_init || !id_init { + if !data_init { log::error!("AcpiScheme::init called multiple times"); } } diff --git a/src/scheme/debug.rs b/src/scheme/debug.rs index 5ce328330b..ce5a8f7509 100644 --- a/src/scheme/debug.rs +++ b/src/scheme/debug.rs @@ -1,15 +1,12 @@ use core::sync::atomic::{AtomicUsize, Ordering}; -use spin::{Once, RwLock}; +use spin::RwLock; use crate::arch::debug::Writer; use crate::event; use crate::scheme::*; use crate::sync::WaitQueue; use crate::syscall::flag::{EventFlags, EVENT_READ, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK}; -use crate::syscall::usercopy::UserSliceRo; -use crate::syscall::usercopy::UserSliceWo; - -static SCHEME_ID: Once = Once::new(); +use crate::syscall::usercopy::{UserSliceRo, UserSliceWo}; static NEXT_ID: AtomicUsize = AtomicUsize::new(0); @@ -30,23 +27,13 @@ pub fn debug_input(data: u8) { // Notify readers of input updates pub fn debug_notify() { - let Some(scheme_id) = SCHEME_ID.get().copied() else { - return; - }; - for (id, _handle) in HANDLES.read().iter() { - event::trigger(scheme_id, *id, EVENT_READ); + event::trigger(GlobalSchemes::Debug.scheme_id(), *id, EVENT_READ); } } pub struct DebugScheme; -impl DebugScheme { - pub fn init(scheme_id: SchemeId) { - SCHEME_ID.call_once(|| scheme_id); - } -} - impl KernelScheme for DebugScheme { fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result { if ctx.uid != 0 { diff --git a/src/scheme/memory.rs b/src/scheme/memory.rs index 5aecfe9618..d3f8abb013 100644 --- a/src/scheme/memory.rs +++ b/src/scheme/memory.rs @@ -55,10 +55,6 @@ impl Handle { } impl MemoryScheme { - pub fn new() -> Self { - MemoryScheme - } - pub fn fmap_anonymous(addr_space: &Arc>, map: &Map) -> Result { let span = PageSpan::validate_nonempty(VirtualAddress::new(map.address), map.size).ok_or(Error::new(EINVAL))?; let page_count = NonZeroUsize::new(span.count).ok_or(Error::new(EINVAL))?; diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index 0507877d6f..27f297f404 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -111,9 +111,8 @@ pub struct SchemeList { map: BTreeMap, names: BTreeMap, SchemeId>>, next_ns: usize, - next_id: usize + next_id: usize, } - impl SchemeList { /// Create a new scheme list. pub fn new() -> Self { @@ -122,8 +121,9 @@ impl SchemeList { names: BTreeMap::new(), // Scheme namespaces always start at 1. 0 is a reserved namespace, the null namespace next_ns: 1, - next_id: 1 + next_id: 1 + core::mem::variant_count::(), }; + list.new_null(); list.new_root(); list @@ -136,12 +136,9 @@ impl SchemeList { //TODO: Only memory: is in the null namespace right now. It should be removed when //anonymous mmap's are implemented - self.insert(ns, "memory", |_| KernelSchemes::Memory).unwrap(); + self.insert_global(ns, "memory", GlobalSchemes::Memory).unwrap(); self.insert(ns, "thisproc", |_| KernelSchemes::Proc(Arc::new(ProcScheme::restricted()))).unwrap(); - self.insert(ns, "pipe", |scheme_id| { - PipeScheme::init(scheme_id); - KernelSchemes::Pipe - }).unwrap(); + self.insert_global(ns, "pipe", GlobalSchemes::Pipe).unwrap(); } /// Initialize a new namespace @@ -151,13 +148,10 @@ impl SchemeList { self.names.insert(ns, BTreeMap::new()); self.insert(ns, "", |scheme_id| KernelSchemes::Root(Arc::new(RootScheme::new(ns, scheme_id)))).unwrap(); - self.insert(ns, "event", |_| KernelSchemes::Event).unwrap(); + self.insert_global(ns, "event", GlobalSchemes::Event).unwrap(); self.insert(ns, "itimer", |_| KernelSchemes::ITimer(Arc::new(ITimerScheme::new()))).unwrap(); - self.insert(ns, "memory", |_| KernelSchemes::Memory).unwrap(); - self.insert(ns, "pipe", |scheme_id| { - PipeScheme::init(scheme_id); - KernelSchemes::Pipe - }).unwrap(); + self.insert_global(ns, "memory", GlobalSchemes::Memory).unwrap(); + self.insert_global(ns, "pipe", GlobalSchemes::Pipe).unwrap(); self.insert(ns, "sys", |_| KernelSchemes::Sys(Arc::new(SysScheme::new()))).unwrap(); self.insert(ns, "time", |scheme_id| KernelSchemes::Time(Arc::new(TimeScheme::new(scheme_id)))).unwrap(); @@ -170,28 +164,19 @@ impl SchemeList { let ns = self.new_ns(); // These schemes should only be available on the root - #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] - self.insert(ns, "kernel.acpi", |scheme_id| { - AcpiScheme::init(scheme_id); - KernelSchemes::Acpi - }).unwrap(); - #[cfg(all(any(target_arch = "aarch64")))] { self.insert(ns, "kernel.dtb", |scheme_id| Arc::new(DtbScheme::new(scheme_id))).unwrap(); } - - self.insert(ns, "debug", |scheme_id| { - DebugScheme::init(scheme_id); - KernelSchemes::Debug - }).unwrap(); + #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] + { + self.insert_global(ns, "kernel.acpi", GlobalSchemes::Acpi).unwrap(); + } + self.insert_global(ns, "debug", GlobalSchemes::Debug).unwrap(); self.insert(ns, "irq", |scheme_id| KernelSchemes::Irq(Arc::new(IrqScheme::new(scheme_id)))).unwrap(); self.insert(ns, "proc", |scheme_id| KernelSchemes::Proc(Arc::new(ProcScheme::new(scheme_id)))).unwrap(); self.insert(ns, "thisproc", |_| KernelSchemes::Proc(Arc::new(ProcScheme::restricted()))).unwrap(); - self.insert(ns, "serio", |scheme_id| { - SerioScheme::init(scheme_id); - KernelSchemes::Serio - }).unwrap(); + self.insert_global(ns, "serio", GlobalSchemes::Serio).unwrap(); } pub fn make_ns(&mut self, from: SchemeNamespace, names: impl IntoIterator>) -> Result { @@ -236,6 +221,16 @@ impl SchemeList { None } + pub fn insert_global(&mut self, ns: SchemeNamespace, name: &str, global: GlobalSchemes) -> Result<()> { + let prev = self.names.get_mut(&ns).ok_or(Error::new(ENODEV))?.insert(name.into(), global.scheme_id()); + + if prev.is_some() { + return Err(Error::new(EEXIST)); + } + + Ok(()) + } + /// Create a new scheme. pub fn insert(&mut self, ns: SchemeNamespace, name: &str, scheme_fn: impl FnOnce(SchemeId) -> KernelSchemes) -> Result { self.insert_and_pass(ns, name, |id| (scheme_fn(id), ())).map(|(id, ())| id) @@ -422,18 +417,23 @@ pub fn calc_seek_offset(cur_pos: usize, rel_pos: isize, whence: usize, len: usiz #[derive(Clone)] pub enum KernelSchemes { - Debug, - Event, Irq(Arc), ITimer(Arc), - Memory, - Pipe, Proc(Arc), Root(Arc), - Serio, Sys(Arc), Time(Arc), User(UserScheme), + Global(GlobalSchemes), +} +#[repr(u8)] +#[derive(Clone, Copy)] +pub enum GlobalSchemes { + Debug = 1, + Event, + Memory, + Pipe, + Serio, #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] Acpi, @@ -444,21 +444,35 @@ impl core::ops::Deref for KernelSchemes { fn deref(&self) -> &Self::Target { match self { - Self::Debug => &DebugScheme, - Self::Event => &EventScheme, Self::Irq(scheme) => &**scheme, Self::ITimer(scheme) => &**scheme, - Self::Memory => &MemoryScheme, - Self::Pipe => &PipeScheme, Self::Proc(scheme) => &**scheme, Self::Root(scheme) => &**scheme, - Self::Serio => &SerioScheme, Self::Sys(scheme) => &**scheme, Self::Time(scheme) => &**scheme, Self::User(scheme) => scheme, + Self::Global(global) => &**global, + } + } +} +impl core::ops::Deref for GlobalSchemes { + type Target = dyn KernelScheme; + + fn deref(&self) -> &Self::Target { + match self { + Self::Debug => &DebugScheme, + Self::Event => &EventScheme, + Self::Memory => &MemoryScheme, + Self::Pipe => &PipeScheme, + Self::Serio => &SerioScheme, #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] Self::Acpi => &AcpiScheme, } } } +impl GlobalSchemes { + pub fn scheme_id(self) -> SchemeId { + SchemeId::new(self as usize) + } +} diff --git a/src/scheme/pipe.rs b/src/scheme/pipe.rs index bc5f99da52..73357b0b4a 100644 --- a/src/scheme/pipe.rs +++ b/src/scheme/pipe.rs @@ -3,30 +3,24 @@ use core::sync::atomic::{AtomicUsize, Ordering, AtomicBool}; use alloc::sync::Arc; use alloc::collections::{BTreeMap, VecDeque}; -use spin::{Mutex, Once, RwLock}; +use spin::{Mutex, RwLock}; use crate::event; -use crate::scheme::SchemeId; use crate::sync::WaitCondition; use crate::syscall::error::{Error, Result, EAGAIN, EBADF, EINTR, EINVAL, ENOENT, EPIPE, ESPIPE}; use crate::syscall::flag::{EventFlags, EVENT_READ, EVENT_WRITE, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK, MODE_FIFO}; use crate::syscall::data::Stat; use crate::syscall::usercopy::{UserSliceWo, UserSliceRo}; -use super::{KernelScheme, OpenResult, CallerCtx}; +use super::{KernelScheme, OpenResult, CallerCtx, GlobalSchemes}; // TODO: Preallocate a number of scheme IDs, since there can only be *one* root namespace, and // therefore only *one* pipe scheme. -static SCHEME_ID: Once = Once::new(); static PIPE_NEXT_ID: AtomicUsize = AtomicUsize::new(1); // TODO: SLOB? static PIPES: RwLock>> = RwLock::new(BTreeMap::new()); -pub fn pipe_scheme_id() -> SchemeId { - *SCHEME_ID.get().expect("pipe scheme must be initialized") -} - const MAX_QUEUE_SIZE: usize = 65536; // In almost all places where Rust (and LLVM) uses pointers, they are limited to nonnegative isize, @@ -56,12 +50,6 @@ pub fn pipe(flags: usize) -> Result<(usize, usize)> { pub struct PipeScheme; -impl PipeScheme { - pub fn init(scheme_id: SchemeId) { - SCHEME_ID.call_once(|| scheme_id); - } -} - impl KernelScheme for PipeScheme { fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { let (is_writer_not_reader, key) = from_raw_id(id); @@ -110,7 +98,7 @@ impl KernelScheme for PipeScheme { let (is_write_not_read, key) = from_raw_id(id); let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); - let scheme_id = pipe_scheme_id(); + let scheme_id = GlobalSchemes::Pipe.scheme_id(); let can_remove = if is_write_not_read { event::trigger(scheme_id, key, EVENT_READ); @@ -193,7 +181,7 @@ impl KernelScheme for PipeScheme { let _ = vec.drain(..bytes_read); if bytes_read > 0 { - event::trigger(pipe_scheme_id(), key | WRITE_NOT_READ_BIT, EVENT_WRITE); + event::trigger(GlobalSchemes::Pipe.scheme_id(), key | WRITE_NOT_READ_BIT, EVENT_WRITE); pipe.write_condition.notify(); return Ok(bytes_read); @@ -242,7 +230,7 @@ impl KernelScheme for PipeScheme { } if bytes_written > 0 { - event::trigger(pipe_scheme_id(), key, EVENT_READ); + event::trigger(GlobalSchemes::Pipe.scheme_id(), key, EVENT_READ); pipe.read_condition.notify(); return Ok(bytes_written); diff --git a/src/scheme/serio.rs b/src/scheme/serio.rs index 70e9fd747a..02ea0b827b 100644 --- a/src/scheme/serio.rs +++ b/src/scheme/serio.rs @@ -3,7 +3,7 @@ use core::str; use core::sync::atomic::{AtomicUsize, Ordering}; -use spin::{Once, RwLock}; +use spin::RwLock; use crate::event; use crate::scheme::*; @@ -11,8 +11,6 @@ use crate::sync::WaitQueue; use crate::syscall::flag::{EventFlags, EVENT_READ, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK}; use crate::syscall::usercopy::UserSliceWo; -static SCHEME_ID: Once = Once::new(); - static NEXT_ID: AtomicUsize = AtomicUsize::new(0); /// Input queue @@ -30,23 +28,13 @@ static HANDLES: RwLock> = RwLock::new(BTreeMap::new()); pub fn serio_input(index: usize, data: u8) { INPUT[index].send(data); - let Some(scheme_id) = SCHEME_ID.get().copied() else { - return; - }; - for (id, _handle) in HANDLES.read().iter() { - event::trigger(scheme_id, *id, EVENT_READ); + event::trigger(GlobalSchemes::Serio.scheme_id(), *id, EVENT_READ); } } pub struct SerioScheme; -impl SerioScheme { - pub fn init(scheme_id: SchemeId) { - SCHEME_ID.call_once(|| scheme_id); - } -} - impl KernelScheme for SerioScheme { fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result { if ctx.uid != 0 { diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index 255d07417c..278b2c072d 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -214,7 +214,7 @@ pub fn sendfd(socket: FileHandle, fd: FileHandle, flags_raw: usize, arg: u64) -> let (scheme, number) = match current.get_file(socket).ok_or(Error::new(EBADF))?.description.read() { ref desc => (desc.scheme, desc.number), }; - let scheme = Arc::clone(scheme::schemes().get(scheme).ok_or(Error::new(ENODEV))?); + let scheme = scheme::schemes().get(scheme).ok_or(Error::new(ENODEV))?.clone(); (scheme, number, current.remove_file(fd).ok_or(Error::new(EBADF))?.description) };