diff --git a/oro-arch-x86_64/src/init.rs b/oro-arch-x86_64/src/init.rs index b509ffc8..20e533af 100644 --- a/oro-arch-x86_64/src/init.rs +++ b/oro-arch-x86_64/src/init.rs @@ -337,8 +337,7 @@ pub unsafe fn boot() -> ! { in("rdi") kernel_irq_rsp, in("rsi") kernel_rsp, in("rax") syscall_response.error as u64, - in("rdx") syscall_response.ret1, - in("r9") syscall_response.ret2, + in("r9") syscall_response.ret, } } } diff --git a/oro-arch-x86_64/src/interrupt.rs b/oro-arch-x86_64/src/interrupt.rs index 01f3586d..48a6423e 100644 --- a/oro-arch-x86_64/src/interrupt.rs +++ b/oro-arch-x86_64/src/interrupt.rs @@ -183,8 +183,7 @@ unsafe extern "C" fn isr_sys_timer_rust() -> ! { in("r8") thread_cr3_phys, in("r10") thread_rsp, in("rax") syscall_response.error as u64, - in("rdx") syscall_response.ret1, - in("r9") syscall_response.ret2, + in("r9") syscall_response.ret, options(noreturn) } } diff --git a/oro-arch-x86_64/src/syscall.rs b/oro-arch-x86_64/src/syscall.rs index 400f32fb..1fdd0845 100644 --- a/oro-arch-x86_64/src/syscall.rs +++ b/oro-arch-x86_64/src/syscall.rs @@ -186,8 +186,7 @@ unsafe extern "C" fn syscall_enter_noncompat_rust() -> ! { in("r8") thread_cr3_phys, in("r10") thread_rsp, in("rax") syscall_response.error as u64, - in("rdx") syscall_response.ret1, - in("r9") syscall_response.ret2, + in("r9") syscall_response.ret, options(noreturn) } } diff --git a/oro-arch-x86_64/src/task.rs b/oro-arch-x86_64/src/task.rs index 5722558d..78e65f30 100644 --- a/oro-arch-x86_64/src/task.rs +++ b/oro-arch-x86_64/src/task.rs @@ -152,8 +152,7 @@ pub unsafe extern "C" fn oro_x86_64_kernel_to_user() { /// - `rdi` must be a pointer to the core state `kernel_irq_stack` field. /// - `rsi` must be a pointer to the core state `kernel_stack` field. /// - `rax` must contain the system call error code. -/// - `rdx` must contain the first system call return value. -/// - `r9` must contain the second system call return value. +/// - `r9` must contain the system call return value. /// - `call` must be used to jump to this function. /// /// All registers must be marked as clobbered. @@ -211,6 +210,7 @@ pub unsafe extern "C" fn oro_x86_64_kernel_to_user_sysret() { // // SAFETY(qix-): Vector registers are clobbered AND considered insecurely transferred. // SAFETY(qix-): It is specified that the kernel DOES NOT zero vector registers. + "xor rdx, rdx", "xor r8, r8", "xor r10, r10", "xor r11, r11", @@ -243,8 +243,7 @@ pub unsafe extern "C" fn oro_x86_64_kernel_to_user_sysret() { /// /// - `r8` must be the `cr3` value of the user task. /// - `rax` must contain the system call error code. -/// - `rdx` must contain the first system call return value. -/// - `r9` must contain the second system call return value. +/// - `r9` must contain the system call return value. /// - `r10` must be the user task's IRQ stack pointer. /// /// All registers must be marked as clobbered. @@ -280,6 +279,7 @@ pub unsafe extern "C" fn oro_x86_64_user_to_user_sysret() { // // SAFETY(qix-): Vector registers are clobbered AND considered insecurely transferred. // SAFETY(qix-): It is specified that the kernel DOES NOT zero vector registers. + "xor rdx, rdx", "xor r8, r8", "xor r10, r10", "xor r11, r11", diff --git a/oro-kernel/src/instance.rs b/oro-kernel/src/instance.rs index f7725947..3a85154b 100644 --- a/oro-kernel/src/instance.rs +++ b/oro-kernel/src/instance.rs @@ -14,7 +14,6 @@ use crate::{ arch::{Arch, InstanceHandle}, module::Module, port::Port, - registry::Registry, ring::Ring, thread::Thread, }; @@ -66,8 +65,6 @@ pub struct Instance { ports: Vec>>, /// The instance's architecture handle. handle: A::InstanceHandle, - /// The root object registry for the instance. - registry: Arc>, } impl Instance { @@ -100,7 +97,6 @@ impl Instance { threads: Vec::new(), ports: Vec::new(), handle, - registry: Arc::default(), })); ring.lock().instances.push(r.clone()); @@ -145,10 +141,4 @@ impl Instance { pub fn mapper(&self) -> &UserHandle { self.handle.mapper() } - - /// Returns a handle to the instance's object registry. - #[must_use] - pub fn registry(&self) -> Arc> { - self.registry.clone() - } } diff --git a/oro-kernel/src/registry.rs b/oro-kernel/src/registry.rs index 6888456d..e3fef57c 100644 --- a/oro-kernel/src/registry.rs +++ b/oro-kernel/src/registry.rs @@ -1,5 +1,5 @@ //! Oro kernel object registry implementation. -#![expect(clippy::inline_always)] +#![expect(unused_imports)] use oro_mem::alloc::sync::Arc; use oro_sync::{Lock, ReentrantMutex}; @@ -15,182 +15,4 @@ use crate::{ thread::Thread, }; -/// The root registry object for the Oro kernel. -/// -/// This structure manages the open object handles and is typically -/// scoped to a module instance. -#[derive(Default)] -pub struct Registry { - /// The list of open object handles. - // TODO(qix-): This stash is a stop-gap solution for now. The conversion to/from `usize` is a hack - // TODO(qix-): and will be remedied in the future - namely, to prevent re-use of handles. - handles: Stash>, Tag>, -} - -impl Registry { - /// Looks up a key in the root of the registry (which is a "logical" object, not backed by a handle). - fn get_root_key( - thread: &Arc>>, - key: u64, - ) -> Result>> { - match key { - key!("thread") => Ok(RootThreadObject::from_thread(thread.clone())), - _ => Err(Error::BadKey), - } - } - - /// Attempts to service an [`Opcode::Open`] system call. - fn try_open( - &mut self, - thread: &Arc>>, - handle: u64, - key: u64, - ) -> Result { - let object = if handle == 0 { - Self::get_root_key(thread, key)? - } else { - self.handles - // SAFETY: We have already checked if handle == 0. - .get(unsafe { handle.unchecked_sub(1) }.into()) - .ok_or(Error::BadHandle)? - .lock() - .try_get_object(key)? - }; - - let handle: u64 = self.handles.put(object).into(); - - // SAFETY(qix-): We add one here to ensure that the handle is not 0. - // SAFETY(qix-): Technically, if you exhaust the entirety of a 64-bit handle space, you will - // SAFETY(qix-): cause UB. I'm choosing not to worry about this. If you exhaust the handle space, - // SAFETY(qix-): you have bigger problems. - Ok(unsafe { handle.unchecked_add(1) }) - } - - /// Handles a system call request dispatched, typically, by the scheduler. - /// - /// This function operates in the context of a thread, taking a _reference_ - /// to an `Arc`-wrapped thread handle in case a clone is _not_ needed (e.g. - /// in cases where early-stage validation fails). - pub fn dispatch_system_call( - &mut self, - thread: &Arc>>, - request: &SystemCallRequest, - ) -> SystemCallAction { - match request.opcode { - Opcode::Open => { - match self.try_open(thread, request.arg1, request.arg2) { - Ok(handle) => { - SystemCallAction::RespondImmediate(SystemCallResponse { - error: Error::Ok, - ret1: handle, - ret2: 0, - }) - } - Err(error) => { - SystemCallAction::RespondImmediate(SystemCallResponse { - error, - ret1: 0, - ret2: 0, - }) - } - } - } - _ => { - SystemCallAction::RespondImmediate(SystemCallResponse { - error: Error::BadOpcode, - ret1: 0, - ret2: 0, - }) - } - } - } -} - -/// Represents an object in the object registry. -trait Object { - /// Attempts to retrieve an object from the registry given its - /// parent and a key. - fn try_get_object(&self, key: u64) -> Result>>; -} - -/// Represents the root `thread` object in the object registry. Contextualized around a given thread. -struct RootThreadObject { - /// The context thread handle. - self_thread: Arc>>, -} - -impl RootThreadObject { - /// Creates a new `RootThreadObject` contextualized around the given thread handle. - fn from_thread(self_thread: Arc>>) -> Arc> { - Arc::new(ReentrantMutex::new(Self { self_thread })) - } -} - -impl Object for RootThreadObject { - fn try_get_object(&self, key: u64) -> Result>> { - match key { - key!("self") => Ok(ThreadObject::from_thread(self.self_thread.clone())), - _ => Err(Error::BadKey), - } - } -} - -/// Wraps a [`Thread`] for interaction via the object registry. -struct ThreadObject { - /// The target thread handle. - #[expect(dead_code)] - thread: Arc>>, -} - -impl ThreadObject { - /// Creates a new `ThreadObject` from a thread handle. - fn from_thread(thread: Arc>>) -> Arc> { - Arc::new(ReentrantMutex::new(Self { thread })) - } -} - -impl Object for ThreadObject { - fn try_get_object(&self, key: u64) -> Result>> { - #[expect(clippy::match_single_binding)] - match key { - _ => Err(Error::BadKey), - } - } -} - -/// Inner type for the handle map keys. -/// -/// Stop-gap solution for now; conversion to/from `usize` is a hack and will be remedied in the future. -#[derive(Clone, Copy)] -#[repr(transparent)] -struct Tag(u64); - -impl From for Tag { - #[inline(always)] - fn from(val: usize) -> Self { - ::oro_macro::assert::size_eq::(); - Self(val as u64) - } -} - -impl From for usize { - #[inline(always)] - fn from(val: Tag) -> Self { - ::oro_macro::assert::size_eq::(); - val.0 as usize - } -} - -impl From for Tag { - #[inline(always)] - fn from(val: u64) -> Self { - Self(val) - } -} - -impl From for u64 { - #[inline(always)] - fn from(val: Tag) -> Self { - val.0 - } -} +// TODO(qix-): Implement the registry (this is just here for a moment, bear with) diff --git a/oro-kernel/src/scheduler.rs b/oro-kernel/src/scheduler.rs index fca1e5d8..6b01b3db 100644 --- a/oro-kernel/src/scheduler.rs +++ b/oro-kernel/src/scheduler.rs @@ -198,13 +198,19 @@ impl Scheduler { let coming_from_user = if let Some(thread) = self.current.take() { let t = thread.lock(); + // TODO(qix-): Put back the registry stuff when it's working. let response = { - let instance = t.instance(); - let registry = instance.lock().registry(); - let mut registry_lock = registry.lock(); - let r = registry_lock.dispatch_system_call(&thread, request); - drop(registry_lock); - r + // let instance = t.instance(); + // let registry = instance.lock().registry(); + // let mut registry_lock = registry.lock(); + // drop(registry_lock); + // let r = registry_lock.dispatch_system_call(&thread, request); + // r + let _ = request; + SystemCallAction::RespondImmediate(SystemCallResponse { + error: oro_sysabi::syscall::Error::NotImplemented, + ret: 0, + }) }; match response { @@ -317,10 +323,8 @@ pub struct SystemCallRequest { pub struct SystemCallResponse { /// The error code. pub error: oro_sysabi::syscall::Error, - /// The first return value. - pub ret1: u64, - /// The second return value. - pub ret2: u64, + /// The return value. + pub ret: u64, } /// Response action from the registry after dispatching a system call. diff --git a/oro-std/src/thread/current.rs b/oro-std/src/thread/current.rs index 549bf487..3b4fa05f 100644 --- a/oro-std/src/thread/current.rs +++ b/oro-std/src/thread/current.rs @@ -1,5 +1,6 @@ -#![expect(unused_imports)] -use ::oro::{id::kernel::iface::THREAD_V0, key, uses}; +use core::num::NonZero; + +use ::oro::{id::kernel::iface::THREAD_V0, syscall}; use crate::thread::Thread; @@ -8,12 +9,21 @@ use crate::thread::Thread; /// # Oro-specific /// This function **panics** in the rare case the thread handle cannot be retrieved. /// This is a temporary measure until the kernel implements TLS. +#[expect(clippy::missing_panics_doc)] #[must_use] pub fn current() -> Thread { // NOTE(qix-): The real `std` stores a TLS handle to the current thread, // NOTE(qix-): which is totally valid but the kernel hasn't implemented // NOTE(qix-): TLS quite yet. So we do it (slowly) here each time. - uses!(THREAD_V0, key!("id")); + let id = syscall::get!( + THREAD_V0, + *::oro::shared::THREAD_V0_HANDLE, + 0, + syscall::key!("id") + ) + .expect("failed to retrieve current thread ID"); + + Thread::new(NonZero::new(id).expect("kernel indicated the current thread ID is zero")) } /// Cooperatively gives up a timeslice to the OS scheduler. @@ -32,5 +42,12 @@ pub fn yield_now() { // NOTE(qix-): The real `std` stores a TLS handle to the current thread, // NOTE(qix-): which is totally valid but the kernel hasn't implemented // NOTE(qix-): TLS quite yet. So we do it (slowly) here each time. - uses!(THREAD_V0, key!("id")); + syscall::set!( + THREAD_V0, + *::oro::shared::THREAD_V0_HANDLE, + 0, + syscall::key!("yield"), + 0 + ) + .expect("failed to yield current thread"); } diff --git a/oro-std/src/thread/mod.rs b/oro-std/src/thread/mod.rs index f8a52789..bedb0cad 100644 --- a/oro-std/src/thread/mod.rs +++ b/oro-std/src/thread/mod.rs @@ -47,6 +47,12 @@ pub struct Thread { } impl Thread { + /// Internal method to create a new thread handle. + #[must_use] + pub(crate) fn new(id: NonZero) -> Self { + Self { id: ThreadId(id) } + } + /// Gets the thread’s unique identifier. #[must_use] pub fn id(&self) -> ThreadId { diff --git a/oro-sysabi/src/arch/x86_64.rs b/oro-sysabi/src/arch/x86_64.rs index 20cff822..ac7fbdfc 100644 --- a/oro-sysabi/src/arch/x86_64.rs +++ b/oro-sysabi/src/arch/x86_64.rs @@ -13,18 +13,17 @@ pub unsafe fn syscall( arg2: u64, arg3: u64, arg4: u64, -) -> (syscall::Error, u64, u64) { +) -> (syscall::Error, u64) { let mut err: u64 = opcode as u64; - let mut ret1: u64 = arg3; - let mut ret2: u64 = arg4; + let mut ret: u64 = arg4; asm!( "syscall", inlateout("rax") err, in("rsi") arg1, in("rdi") arg2, - inlateout("rdx") ret1, - inlateout("r9") ret2, + in("rdx") arg3, + inlateout("r9") ret, lateout("rcx") _, lateout("r8") _, lateout("r10") _, @@ -47,5 +46,5 @@ pub unsafe fn syscall( lateout("zmm15") _, ); - (transmute::(err), ret1, ret2) + (transmute::(err), ret) } diff --git a/oro-sysabi/src/id.rs b/oro-sysabi/src/id.rs index 9567629e..c6362bf9 100644 --- a/oro-sysabi/src/id.rs +++ b/oro-sysabi/src/id.rs @@ -46,4 +46,7 @@ pub mod meta { /// ID indicating that the following metadata indicates an interface/key usage. pub const USES: u64 = KERNEL_ID_TYPE_META | 0x00_001; + + /// ID indicating that the following metadata indicates an interface slot. + pub const IFACE_SLOT: u64 = KERNEL_ID_TYPE_META | 0x00_002; } diff --git a/oro-sysabi/src/lib.rs b/oro-sysabi/src/lib.rs index 568962d0..ad8bcbf3 100644 --- a/oro-sysabi/src/lib.rs +++ b/oro-sysabi/src/lib.rs @@ -2,9 +2,8 @@ #![cfg_attr(not(test), no_std)] #![cfg_attr(doc, feature(doc_cfg, doc_auto_cfg))] -mod macros; - pub(crate) mod arch; pub mod id; +pub mod macros; pub mod syscall; diff --git a/oro-sysabi/src/macros.rs b/oro-sysabi/src/macros.rs index 21515d4a..f41b85a6 100644 --- a/oro-sysabi/src/macros.rs +++ b/oro-sysabi/src/macros.rs @@ -3,13 +3,156 @@ /// Auxiliary types used by macros exported by this crate. /// /// **Using this module directly is highly discouraged. It is not stable.** -pub mod private {} +pub mod private { + /// Indicates to the kernel that an `(interface_id, key)` is to be used + /// via system calls. + #[must_use] + pub const fn uses(iface: u64, key: u64) -> [u64; 4] { + [crate::id::meta::USES, 2, iface, key] + } + + /// Indicates to the kernel that it should allocate a slot for importing + /// an interface, anonymously. + #[must_use] + pub const fn interface_slot(iface: u64) -> ([u64; 4], usize) { + ([crate::id::meta::IFACE_SLOT, 2, 0, iface], 2) + } + + /// Indicates to the kernel that it should allocate a slot for importing + /// an interface, with a key. + #[must_use] + pub const fn interface_slot_key(iface: u64, key: u64) -> ([u64; 5], usize) { + ([crate::id::meta::IFACE_SLOT, 3, 0, iface, key], 2) + } + + /// Stub for the `must_use` attribute. This is identical to [`core::hint::must_use`]. + #[expect(clippy::inline_always)] + #[must_use] + #[inline(always)] + pub const fn must_use(e: T) -> T { + e + } +} + +/// Converts a literal string into a 64-bit object key. +/// +/// # Panics +/// Panics if the string is not 8 bytes or less. +#[macro_export] +macro_rules! key { + ($key:literal) => { + const { + const KEY_RAW: &str = $key; + + assert!( + KEY_RAW.len() <= 8, + concat!("object keys too long (must be <= 8 bytes): ", $key) + ); + + const KEY: &str = concat!($key, "\0\0\0\0\0\0\0\0"); + + let bytes = KEY.as_bytes(); + + ((bytes[0] as u64) << 56) + | ((bytes[1] as u64) << 48) + | ((bytes[2] as u64) << 40) + | ((bytes[3] as u64) << 32) + | ((bytes[4] as u64) << 24) + | ((bytes[5] as u64) << 16) + | ((bytes[6] as u64) << 8) + | (bytes[7] as u64) + } + }; +} /// Declares that an `(interface_id, key)` is to be used /// by the current function. +/// +/// This macro is meant to be used **once** directly before +/// any system call callsites. +/// +/// # Linker Section +/// Note that this macro generates a static array that is placed +/// into `.oro.uses` linker section. The `oro-sysabi` crate does +/// **not** provide a linker script to automatically include this +/// section. +/// +/// It is recommended to use the `oro` crate as it's higher level +/// and handles much of this for you. #[macro_export] macro_rules! uses { + ($iface:expr, $key:expr) => {{ + const LEN: usize = $crate::macros::private::uses(const { $iface }, const { $key }).len(); + #[link_section = ".oro.uses"] + static USES: [u64; LEN] = $crate::macros::private::uses(const { $iface }, const { $key }); + + ::core::hint::black_box(USES); + }}; +} + +/// Declares that an interface slot is to be allocated for the module. +/// +/// Should be used once per slot; **these are not idempotent like `uses!`**. +/// +/// Can optionally take a key, which will be used to identify the slot to +/// the user; otherwise, the slot receives an anonymous (numeric) key. +/// +/// # Linker Section +/// Note that this macro generates a static array that is placed +/// into `.oro.uses` linker section. The `oro-sysabi` crate does +/// **not** provide a linker script to automatically include this +/// section. +/// +/// It is recommended to use the `oro` crate as it's higher level +/// and handles much of this for you. +#[macro_export] +macro_rules! interface_slot { + ($iface:expr) => { + const { + const SLOT_AND_IDX: ([u64; 4], usize) = + $crate::macros::private::interface_slot(const { $iface }); + const SLOT_IDX: usize = SLOT_AND_IDX.1; + #[link_section = ".oro.uses"] + static SLOT: [u64; SLOT_AND_IDX.0.len()] = SLOT_AND_IDX.0; + $crate::macros::private::must_use(&SLOT[SLOT_IDX]) + } + }; ($iface:expr, $key:expr) => { - todo!(); + const { + const SLOT_AND_IDX: ([u64; 5], usize) = + $crate::macros::private::interface_slot_key(const { $iface }, const { $key }); + const SLOT_IDX: usize = SLOT_AND_IDX.1; + #[link_section = ".oro.uses"] + static SLOT: [u64; SLOT_AND_IDX.0.len()] = SLOT_AND_IDX.0; + $crate::macros::private::must_use(&SLOT[SLOT_IDX]) + } }; } + +/// Performs a `GET` system call to the kernel, automatically emitting +/// a [`uses!`] call for the `(interface_id, key)` pair. +/// +/// Requires that the arguments to `interface_id` and `key` are constants. +/// If you need to use runtime values, use [`crate::syscall::get`] instead, +/// and manually call [`uses!`] before the call. +#[macro_export] +macro_rules! syscall_get { + ($interface_id:expr, $interface_handle:expr, $index:expr, $key:expr) => {{ + $crate::uses!($interface_id, $key); + $crate::syscall::get($interface_handle, $index, $key) + }}; +} + +/// Performs a `SET` system call to the kernel, automatically emitting +/// a [`uses!`] call for the `(interface_id, key)` pair. +/// +/// Requires that the arguments to `interface_id` and `key` are constants. +/// If you need to use runtime values, use [`crate::syscall::set`] instead, +/// and manually call [`uses!`] before the call. +#[macro_export] +macro_rules! syscall_set { + ($interface_id:expr, $interface_handle:expr, $index:expr, $key:expr, $value:expr) => {{ + $crate::uses!($interface_id, $key); + $crate::syscall::set($interface_handle, $index, $key, $value) + }}; +} diff --git a/oro-sysabi/src/syscall.rs b/oro-sysabi/src/syscall.rs index 7b760eea..8e75268d 100644 --- a/oro-sysabi/src/syscall.rs +++ b/oro-sysabi/src/syscall.rs @@ -2,12 +2,16 @@ #![expect(clippy::inline_always)] /// Type alias for a syscall result that may return an [`Error`] code. -pub type Result = core::result::Result; +/// +/// In some cases, extended error information is returned via the second value +/// (namely, interface-specific errors). +pub type Result = core::result::Result; /// Error codes returned by system calls. /// -/// These error codes are architecture-independent, -/// and are the same across all architectures. +/// These error codes are architecture-independent (except for the case +/// of arch-dependent interfaces via [`Error::InterfaceError`]) and are +/// otherwise the same across all architectures. /// /// # Non-exhaustive /// For both backwards and forwards compatibility, this enum is marked as non-exhaustive. @@ -17,17 +21,6 @@ pub type Result = core::result::Result; /// As such, match arms should always have a catch-all arm (`_ => { ... }`) to handle otherwise /// unknown error codes. /// -/// # Error Permanence -/// Error codes are marked as either **temporary** or **permanent** (sometimes determined by the -/// table upon which is being operated). -/// -/// Temporary errors may be resolved by retrying the operation at a later time, or by changing -/// the operation parameters. -/// -/// Permanent errors are not expected to be resolved with the same parameters, possibly ever. -/// In some select cases (marked as such), the error may be resolved in a newer version of the kernel, -/// but application code should treat these errors as indicators of likely bugs or misconfigurations. -/// /// # Error Precedence /// There is no stability or guarantee on the precedence of error codes. Applications should /// make no assumptions or inferrences about the parameters or context based on which error code @@ -48,46 +41,27 @@ pub enum Error { // NOTE(qix-): // NOTE(qix-): Removed error codes should be marked as deprecated and kept // NOTE(qix-): around until it's certain that no syscalls would return them. - /// The requested operation completed successfully. **Not an error.** - /// - /// This error is **temporary**. - Ok = 0, + /// The requested operation completed successfully. **Not an error.** The returned + /// value(s), if any, are successful response data from the operation. + Ok = 0, /// The operation code is invalid. - /// - /// This error is **permanent**. - BadOpcode = 1, + BadOpcode = 1, /// The requested operation code is valid, but some portion of the operation /// is not implemented in the current version of the kernel (or environment). - /// - /// This error is **permanent**. - NotImplemented = 2, + NotImplemented = 2, + /// The requested interface ID does not exist, or is not available in the caller's + /// context. + BadInterface = 3, /// The requested key does not exist, or is outside the bounds of the object /// (for list). - /// - /// This error is **temporary**. - BadKey = 3, + BadKey = 4, + /// The requested index does not exist. + BadIndex = 5, /// The given key is read-only and cannot be modified. - /// - /// This error is **permanent**. - ReadOnly = 4, - /// The object is not a list; the requested opcode cannot be performed on it. - /// - /// This error is **permanent**. - NotList = 5, - /// The requested type is mismatched for the given key. - /// - /// This error is **permanent**. - WrongType = 6, - /// The requested version mismatched. - /// - /// This error is **permanent**. - VersionMismatch = 7, - /// The given handle is invalid. - /// - /// This error is **temporary** (really, _permanent_ but technically _temporary_ since - /// it's unlikely that a bad handle is passed, an open operation returns that exact handle, - /// and passing the previously bad handle results in a system call that is meaningfully the same). - BadHandle = 8, + ReadOnly = 6, + /// The interface returned an interface-specific error; check the `value` + /// code for that error. + InterfaceError = 0xFFFF_FFFF_FFFF_FFFF, } /// Each individual operation that can be performed by the Oro registry. @@ -95,198 +69,98 @@ pub enum Error { #[repr(u64)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Opcode { - /// Opens an object for reading and writing. - Open = 0x8888_0000_0000_0000, - /// Closes an object handle opened with [`Opcode::Open`]. - Close = 0x8888_0000_0000_0001, /// Gets a value. - Get = 0x8888_0000_0000_0002, + Get = 0x8888_0000_0000_0001, /// Sets a value. - Set = 0x8888_0000_0000_0003, + Set = 0x8888_0000_0000_0002, } /// Performs a branchless system call to get a registry value by key. /// -/// Use [`reg_get`] for a safer alternative. +/// Use [`get`] for a safer alternative. /// -/// Returns a tuple of the error code, the value, and the version. -/// The value and version are only valid if the error code is [`Error::Ok`]. +/// Returns a tuple of the error code and the value. /// /// # Safety -/// Any interpretation of the result value (first tuple value) is undefined -/// if the error is not [`Error::Ok`]. +/// The interpretation of the result value (second tuple value) is dependent upon +/// the `Error` code returned. If the error code is [`Error::Ok`], the value returned +/// is the returned value from the registry. +/// +/// Otherwise, the value is `0` for any `Error` other than [`Error::InterfaceError`], +/// in which case the value's meaning holds the error passed back by the interface; +/// consult the interface's documentation for the meaning of that error code. #[inline(always)] #[must_use] -pub unsafe fn reg_get_raw(object_id: u64, key: u64, version: u64) -> (Error, u64, u64) { +pub unsafe fn get_raw(interface_id: u64, index: u64, key: u64) -> (Error, u64) { #[cfg(target_arch = "x86_64")] { - crate::arch::x86_64::syscall(Opcode::Get, object_id, key, 0, version) + crate::arch::x86_64::syscall(Opcode::Get, interface_id, index, key, 0) } #[cfg(not(target_arch = "x86_64"))] { // NOTE(qix-): Avoids unused variable warnings. - let _ = object_id; + let _ = interface_id; + let _ = index; let _ = key; - let _ = version; - (Error::NotImplemented, 0, 0) + (Error::NotImplemented, 0) } } /// Performs a branchless system call to set a registry value by key. /// -/// Use [`reg_set`] for a safer alternative. +/// Use [`set`] for a safer alternative. /// /// Returns a tuple of the error code and the new version. /// The new version is only valid if the error code is [`Error::Ok`]. /// /// # Safety -/// The error code returned may be [`Error::Ok`] and should be checked. -#[inline(always)] -#[must_use] -pub unsafe fn reg_set_raw(object_id: u64, key: u64, value: u64, version: u64) -> (Error, u64) { - #[cfg(target_arch = "x86_64")] - { - let (err, _, version) = - crate::arch::x86_64::syscall(Opcode::Set, object_id, key, value, version); - (err, version) - } - - #[cfg(not(target_arch = "x86_64"))] - { - // NOTE(qix-): Avoids unused variable warnings. - let _ = object_id; - let _ = key; - let _ = version; - let _ = value; - (Error::NotImplemented, 0) - } -} - -/// Performs a branchless system call to open an object. -/// -/// Use [`reg_open`] for a safer alternative. -/// -/// Returns the object's handle. +/// The interpretation of the result value (second tuple value) is dependent upon +/// the `Error` code returned. If the error code is [`Error::Ok`], the value returned +/// is `0`. /// -/// # Safety -/// The returned handle is only valid if the error code is [`Error::Ok`]. +/// Otherwise, the value is `0` for any `Error` other than [`Error::InterfaceError`], +/// in which case the value's meaning holds the error passed back by the interface; +/// consult the interface's documentation for the meaning of that error code. #[inline(always)] #[must_use] -pub unsafe fn reg_open_raw(object_id: u64, key: u64) -> (Error, u64) { +pub unsafe fn set_raw(interface_id: u64, index: u64, key: u64, value: u64) -> (Error, u64) { #[cfg(target_arch = "x86_64")] { - let (err, handle, _) = crate::arch::x86_64::syscall(Opcode::Open, object_id, key, 0, 0); - (err, handle) + crate::arch::x86_64::syscall(Opcode::Set, interface_id, index, key, value) } #[cfg(not(target_arch = "x86_64"))] { // NOTE(qix-): Avoids unused variable warnings. - let _ = object_id; + let _ = interface_id; + let _ = index; let _ = key; + let _ = value; (Error::NotImplemented, 0) } } -/// Performs a branchless system call to close an object. -/// -/// Use [`reg_close`] for a safer alternative. -/// -/// # Safety -/// The error code returned may be [`Error::Ok`] and should be checked. -#[inline(always)] -#[must_use] -pub unsafe fn reg_close_raw(handle: u64) -> Error { - #[cfg(target_arch = "x86_64")] - { - let (err, _, _) = crate::arch::x86_64::syscall(Opcode::Close, handle, 0, 0, 0); - err - } - - #[cfg(not(target_arch = "x86_64"))] - { - // NOTE(qix-): Avoids unused variable warnings. - let _ = handle; - Error::NotImplemented - } -} - /// Gets a registry value by key. /// -/// Returns a tuple of the value and the version. +/// Returns the value. #[inline(always)] -pub fn reg_get(object_id: u64, key: u64, version: u64) -> Result<(u64, u64)> { - let (err, value, version) = unsafe { reg_get_raw(object_id, key, version) }; +pub fn get(interface_id: u64, index: u64, key: u64) -> Result { + let (err, value) = unsafe { get_raw(interface_id, index, key) }; if err == Error::Ok { - Ok((value, version)) + Ok(value) } else { - Err(err) + Err((err, value)) } } /// Sets a registry value by key. -/// -/// Returns the new version of the value. #[inline(always)] -pub fn reg_set(object_id: u64, key: u64, value: u64, version: u64) -> Result { - let (err, version) = unsafe { reg_set_raw(object_id, key, value, version) }; +pub fn set(interface_id: u64, index: u64, key: u64, value: u64) -> Result<()> { + let (err, value) = unsafe { set_raw(interface_id, index, key, value) }; if err == Error::Ok { - Ok(version) + Ok(()) } else { - Err(err) + Err((err, value)) } } - -/// Opens an object for reading and writing. -/// -/// Returns the object's handle. -/// -/// Must be closed with [`reg_close`] when no longer needed. -#[inline(always)] -pub fn reg_open(object_id: u64, key: u64) -> Result { - let (err, handle) = unsafe { reg_open_raw(object_id, key) }; - if err == Error::Ok { - Ok(handle) - } else { - Err(err) - } -} - -/// Closes an object handle. -#[inline(always)] -pub fn reg_close(handle: u64) -> Result<()> { - let err = unsafe { reg_close_raw(handle) }; - if err == Error::Ok { Ok(()) } else { Err(err) } -} - -/// Converts a literal string into a 64-bit object key. -/// -/// # Panics -/// Panics if the string is not 8 bytes or less. -#[macro_export] -macro_rules! key { - ($key:literal) => { - const { - const KEY_RAW: &str = $key; - - assert!( - KEY_RAW.len() <= 8, - concat!("object keys too long (must be <= 8 bytes): ", $key) - ); - - const KEY: &str = concat!($key, "\0\0\0\0\0\0\0\0"); - - let bytes = KEY.as_bytes(); - - ((bytes[0] as u64) << 56) - | ((bytes[1] as u64) << 48) - | ((bytes[2] as u64) << 40) - | ((bytes[3] as u64) << 32) - | ((bytes[4] as u64) << 24) - | ((bytes[5] as u64) << 16) - | ((bytes[6] as u64) << 8) - | (bytes[7] as u64) - } - }; -} diff --git a/oro/src/runtime.rs b/oro/src/runtime.rs index 6f91945d..33cca331 100644 --- a/oro/src/runtime.rs +++ b/oro/src/runtime.rs @@ -1,7 +1,6 @@ //! High-level runtime support for Oro modules. pub use ::oro_sysabi as sysabi; -pub use sysabi::{key, uses}; #[cfg(feature = "panic_handler")] #[panic_handler] @@ -49,9 +48,7 @@ pub unsafe fn terminate() -> ! { use crate::sysabi::{key, syscall as s}; // SAFETY: MUST NOT PANIC. - let _ = s::reg_open(0, key!("thread")) - .and_then(|thread_table_handle| s::reg_open(thread_table_handle, key!("self"))) - .and_then(|thread_handle| s::reg_set(thread_handle, key!("kill"), 1, u64::MAX)); + let _ = s::set_raw(*crate::shared::THREAD_V0_HANDLE, 0, key!("kill"), 1); force_crash() } @@ -123,3 +120,19 @@ pub mod id { // TODO(qix-): Intentionally empty for now. } } + +/// Syscall helper macros. +/// +/// These are just re-exports from [`oro_sysabi::macros`]. +pub mod syscall { + pub use ::oro_sysabi::{interface_slot, key, syscall_get as get, syscall_set as set, uses}; +} + +/// Shared interface slots (for applications that don't need to create their own). +pub mod shared { + /// The shared thread interface. + /// + /// Used by the `oro` runtime to kill off the current thread when `main()` returns. + pub static THREAD_V0_HANDLE: &u64 = + crate::syscall::interface_slot!(crate::id::kernel::iface::THREAD_V0); +}