Skip to content

Commit

Permalink
oro+oro-std+kernel+sysabi+x86_64: flesh out initial syscall interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
Qix- committed Jan 15, 2025
1 parent 1ff74a9 commit 28143f9
Show file tree
Hide file tree
Showing 15 changed files with 280 additions and 413 deletions.
3 changes: 1 addition & 2 deletions oro-arch-x86_64/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions oro-arch-x86_64/src/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
3 changes: 1 addition & 2 deletions oro-arch-x86_64/src/syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
8 changes: 4 additions & 4 deletions oro-arch-x86_64/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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",
Expand Down
10 changes: 0 additions & 10 deletions oro-kernel/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use crate::{
arch::{Arch, InstanceHandle},
module::Module,
port::Port,
registry::Registry,
ring::Ring,
thread::Thread,
};
Expand Down Expand Up @@ -66,8 +65,6 @@ pub struct Instance<A: Arch> {
ports: Vec<Arc<ReentrantMutex<Port>>>,
/// The instance's architecture handle.
handle: A::InstanceHandle,
/// The root object registry for the instance.
registry: Arc<ReentrantMutex<Registry>>,
}

impl<A: Arch> Instance<A> {
Expand Down Expand Up @@ -100,7 +97,6 @@ impl<A: Arch> Instance<A> {
threads: Vec::new(),
ports: Vec::new(),
handle,
registry: Arc::default(),
}));

ring.lock().instances.push(r.clone());
Expand Down Expand Up @@ -145,10 +141,4 @@ impl<A: Arch> Instance<A> {
pub fn mapper(&self) -> &UserHandle<A> {
self.handle.mapper()
}

/// Returns a handle to the instance's object registry.
#[must_use]
pub fn registry(&self) -> Arc<ReentrantMutex<Registry>> {
self.registry.clone()
}
}
182 changes: 2 additions & 180 deletions oro-kernel/src/registry.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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<Arc<ReentrantMutex<dyn Object>>, 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<A: Arch>(
thread: &Arc<ReentrantMutex<Thread<A>>>,
key: u64,
) -> Result<Arc<ReentrantMutex<dyn Object>>> {
match key {
key!("thread") => Ok(RootThreadObject::from_thread(thread.clone())),
_ => Err(Error::BadKey),
}
}

/// Attempts to service an [`Opcode::Open`] system call.
fn try_open<A: Arch>(
&mut self,
thread: &Arc<ReentrantMutex<Thread<A>>>,
handle: u64,
key: u64,
) -> Result<u64> {
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<A: Arch>(
&mut self,
thread: &Arc<ReentrantMutex<Thread<A>>>,
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<Arc<ReentrantMutex<dyn Object>>>;
}

/// Represents the root `thread` object in the object registry. Contextualized around a given thread.
struct RootThreadObject<A: Arch> {
/// The context thread handle.
self_thread: Arc<ReentrantMutex<Thread<A>>>,
}

impl<A: Arch> RootThreadObject<A> {
/// Creates a new `RootThreadObject` contextualized around the given thread handle.
fn from_thread(self_thread: Arc<ReentrantMutex<Thread<A>>>) -> Arc<ReentrantMutex<dyn Object>> {
Arc::new(ReentrantMutex::new(Self { self_thread }))
}
}

impl<A: Arch> Object for RootThreadObject<A> {
fn try_get_object(&self, key: u64) -> Result<Arc<ReentrantMutex<dyn Object>>> {
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<A: Arch> {
/// The target thread handle.
#[expect(dead_code)]
thread: Arc<ReentrantMutex<Thread<A>>>,
}

impl<A: Arch> ThreadObject<A> {
/// Creates a new `ThreadObject` from a thread handle.
fn from_thread(thread: Arc<ReentrantMutex<Thread<A>>>) -> Arc<ReentrantMutex<dyn Object>> {
Arc::new(ReentrantMutex::new(Self { thread }))
}
}

impl<A: Arch> Object for ThreadObject<A> {
fn try_get_object(&self, key: u64) -> Result<Arc<ReentrantMutex<dyn Object>>> {
#[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<usize> for Tag {
#[inline(always)]
fn from(val: usize) -> Self {
::oro_macro::assert::size_eq::<usize, u64>();
Self(val as u64)
}
}

impl From<Tag> for usize {
#[inline(always)]
fn from(val: Tag) -> Self {
::oro_macro::assert::size_eq::<usize, u64>();
val.0 as usize
}
}

impl From<u64> for Tag {
#[inline(always)]
fn from(val: u64) -> Self {
Self(val)
}
}

impl From<Tag> 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)
24 changes: 14 additions & 10 deletions oro-kernel/src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,19 @@ impl<A: Arch> Scheduler<A> {
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 {
Expand Down Expand Up @@ -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.
Expand Down
25 changes: 21 additions & 4 deletions oro-std/src/thread/current.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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.
Expand All @@ -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");
}
6 changes: 6 additions & 0 deletions oro-std/src/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u64>) -> Self {
Self { id: ThreadId(id) }
}

/// Gets the thread’s unique identifier.
#[must_use]
pub fn id(&self) -> ThreadId {
Expand Down
Loading

0 comments on commit 28143f9

Please sign in to comment.