203 lines
5.8 KiB
Rust
203 lines
5.8 KiB
Rust
use core::fmt;
|
|
|
|
use alloc::boxed::Box;
|
|
use yggdrasil_abi::{arch::SavedFrame, error::Error, process::ExitCode};
|
|
|
|
use crate::mem::{KernelTableManager, PhysicalMemoryAllocator};
|
|
|
|
pub trait Scheduler {
|
|
type ThreadId: Copy;
|
|
|
|
fn for_cpu(index: usize) -> &'static Self;
|
|
fn for_affinity_mask(mask: u64) -> &'static Self;
|
|
fn local() -> &'static Self;
|
|
|
|
fn is_local(&self) -> bool;
|
|
fn push(&self, task: Self::ThreadId);
|
|
|
|
/// Selects a new thread from the queue and performs a context switch if necessary.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Only meant to be called from within the timer handler or the thread impl.
|
|
unsafe fn yield_cpu(&self) -> bool;
|
|
}
|
|
|
|
/// Conversion trait to allow multiple kernel closure return types
|
|
pub trait Termination {
|
|
/// Converts the closure return type into [ExitCode]
|
|
fn into_exit_code(self) -> ExitCode;
|
|
}
|
|
|
|
/// Interface for task state save/restore mechanisms
|
|
pub trait TaskFrame {
|
|
/// Creates a "snapshot" of a exception/syscall frame
|
|
fn store(&self) -> SavedFrame;
|
|
|
|
/// Restores the exception/syscall frame from its saved state
|
|
fn restore(&mut self, saved: &SavedFrame);
|
|
|
|
/// Replaces the return value in the frame (or does nothing, if the frame is not a part of a
|
|
/// syscall signal handler)
|
|
fn set_return_value(&mut self, value: u64);
|
|
|
|
/// Replaces the userspace stack pointer in the frame
|
|
fn set_user_sp(&mut self, value: usize);
|
|
|
|
/// Replaces the userspace instruction pointer in the frame
|
|
fn set_user_ip(&mut self, value: usize);
|
|
|
|
/// Replaces the argument in the frame
|
|
fn set_argument(&mut self, value: u64);
|
|
|
|
/// Returns the argument (if any) of the frame being processed
|
|
fn argument(&self) -> u64;
|
|
|
|
/// Returns the userspace stack pointer
|
|
fn user_sp(&self) -> usize;
|
|
/// Returns the userspace instruction pointer
|
|
fn user_ip(&self) -> usize;
|
|
|
|
fn set_single_step(&mut self, step: bool);
|
|
}
|
|
|
|
/// Interface for performing context fork operations
|
|
pub trait ForkFrame<K: KernelTableManager, PA: PhysicalMemoryAllocator>: Sized {
|
|
type Context: TaskContext<K, PA>;
|
|
|
|
/// Constructs a "forked" task context by copying the registers from this one and supplying a
|
|
/// new address space to it.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Unsafe: accepts raw frames and address space address.
|
|
unsafe fn fork(&self, address_space: u64) -> Result<Self::Context, Error>;
|
|
|
|
/// Replaces the return value inside the frame with a new one
|
|
fn set_return_value(&mut self, value: u64);
|
|
}
|
|
|
|
pub struct UserContextInfo {
|
|
pub entry: usize,
|
|
pub argument: usize,
|
|
pub stack_pointer: usize,
|
|
pub thread_pointer: usize,
|
|
pub address_space: u64,
|
|
pub asid: u64,
|
|
pub single_step: bool,
|
|
}
|
|
|
|
/// Platform-specific task context implementation
|
|
pub trait TaskContext<K: KernelTableManager, PA: PhysicalMemoryAllocator>: Sized {
|
|
/// Number of bytes to offset the signal stack pointer by
|
|
const SIGNAL_STACK_EXTRA_ALIGN: usize;
|
|
/// Number of bytes to offset the user stack pointer by
|
|
const USER_STACK_EXTRA_ALIGN: usize;
|
|
|
|
/// Constructs a kernel-space task context
|
|
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error>;
|
|
|
|
/// Constructs a user thread context. The caller is responsible for allocating the userspace
|
|
/// stack and setting up a valid address space for the context.
|
|
fn user(context: UserContextInfo) -> Result<Self, Error>;
|
|
|
|
/// Performs an entry into a context.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Only meant to be called from the scheduler code.
|
|
unsafe fn enter(&self) -> !;
|
|
|
|
/// Performs a context switch between two contexts.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Only meant to be called from the scheduler code.
|
|
unsafe fn switch(&self, from: &Self);
|
|
|
|
/// Performs a context switch and drops the source thread.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Only meant to be called from the scheduler code after the `thread` has terminated.
|
|
unsafe fn switch_and_drop(&self, thread: *const ());
|
|
|
|
/// Replaces the current thread pointer with the provided one.
|
|
fn set_thread_pointer(&self, tp: usize);
|
|
|
|
// XXX
|
|
/// Constructs a safe wrapper process to execute a kernel-space closure
|
|
fn kernel_closure<F: FnOnce() -> ! + Send + 'static>(f: F) -> Result<Self, Error> {
|
|
extern "C" fn closure_wrapper<F: FnOnce() -> ! + Send + 'static>(
|
|
mut closure_addr: usize,
|
|
) -> ! {
|
|
if closure_addr == 0 {
|
|
closure_addr = align_of::<F>();
|
|
}
|
|
let closure = unsafe { Box::from_raw(closure_addr as *mut F) };
|
|
closure()
|
|
}
|
|
|
|
let closure = Box::new(f);
|
|
let ptr = Box::into_raw(closure) as usize;
|
|
Self::kernel(closure_wrapper::<F>, ptr)
|
|
}
|
|
|
|
fn align_stack_for_entry(sp: usize) -> usize {
|
|
sp
|
|
}
|
|
}
|
|
|
|
pub struct StackBuilder {
|
|
base: usize,
|
|
sp: usize,
|
|
}
|
|
|
|
impl StackBuilder {
|
|
pub fn new(base: usize, size: usize) -> Self {
|
|
Self {
|
|
base,
|
|
sp: base + size,
|
|
}
|
|
}
|
|
|
|
pub fn push(&mut self, value: usize) {
|
|
if self.sp == self.base {
|
|
panic!();
|
|
}
|
|
self.sp -= size_of::<usize>();
|
|
unsafe {
|
|
(self.sp as *mut usize).write_volatile(value);
|
|
}
|
|
}
|
|
|
|
pub fn build(self) -> usize {
|
|
self.sp
|
|
}
|
|
}
|
|
|
|
impl<T, E: fmt::Debug> Termination for Result<T, E> {
|
|
fn into_exit_code(self) -> ExitCode {
|
|
match self {
|
|
Ok(_) => ExitCode::SUCCESS,
|
|
Err(_err) => {
|
|
// XXX
|
|
// log::warn!("Kernel thread failed: {:?}", err);
|
|
ExitCode::Exited(1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Termination for ExitCode {
|
|
fn into_exit_code(self) -> ExitCode {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Termination for () {
|
|
fn into_exit_code(self) -> ExitCode {
|
|
ExitCode::SUCCESS
|
|
}
|
|
}
|