feature: add threads (WIP)
This commit is contained in:
parent
6bb4f38edc
commit
adb95ac52e
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -250,6 +250,7 @@ checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80"
|
|||||||
name = "user"
|
name = "user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
"libusr",
|
"libusr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use crate::arch::machine;
|
|||||||
use crate::debug::Level;
|
use crate::debug::Level;
|
||||||
use crate::dev::irq::{IntController, IrqContext};
|
use crate::dev::irq::{IntController, IrqContext};
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
use crate::proc::{sched, Process};
|
use crate::proc::{sched, Thread, Process};
|
||||||
use crate::syscall;
|
use crate::syscall;
|
||||||
use cortex_a::registers::{ESR_EL1, FAR_EL1};
|
use cortex_a::registers::{ESR_EL1, FAR_EL1};
|
||||||
use libsys::{abi, signal::Signal};
|
use libsys::{abi, signal::Signal};
|
||||||
@ -90,7 +90,8 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
|||||||
let far = FAR_EL1.get() as usize;
|
let far = FAR_EL1.get() as usize;
|
||||||
|
|
||||||
if far < mem::KERNEL_OFFSET && sched::is_ready() {
|
if far < mem::KERNEL_OFFSET && sched::is_ready() {
|
||||||
let proc = Process::current();
|
let thread = Thread::current();
|
||||||
|
let proc = thread.owner().unwrap();
|
||||||
|
|
||||||
if proc
|
if proc
|
||||||
.manipulate_space(|space| space.try_cow_copy(far))
|
.manipulate_space(|space| space.try_cow_copy(far))
|
||||||
@ -98,7 +99,7 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
|||||||
{
|
{
|
||||||
// Kill program
|
// Kill program
|
||||||
dump_data_abort(Level::Error, esr, far as u64);
|
dump_data_abort(Level::Error, esr, far as u64);
|
||||||
proc.enter_signal(Signal::SegmentationFault);
|
proc.enter_fault_signal(thread, Signal::SegmentationFault);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -138,6 +139,11 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sched::is_ready() {
|
||||||
|
let thread = Thread::current();
|
||||||
|
errorln!("Unhandled exception in thread {}, {:?}", thread.id(), thread.owner().map(|e| e.id()));
|
||||||
|
}
|
||||||
|
|
||||||
errorln!(
|
errorln!(
|
||||||
"Unhandled exception at ELR={:#018x}, ESR={:#010x}",
|
"Unhandled exception at ELR={:#018x}, ESR={:#010x}",
|
||||||
exc.elr_el1,
|
exc.elr_el1,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use crate::config::{ConfigKey, CONFIG};
|
use crate::config::{ConfigKey, CONFIG};
|
||||||
use crate::fs::{devfs, MemfsBlockAlloc};
|
use crate::fs::{devfs, MemfsBlockAlloc};
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
use crate::proc::{elf, Process};
|
use crate::proc::{wait, elf, Process};
|
||||||
use libsys::stat::{FileDescriptor, OpenFlags};
|
use libsys::stat::{FileDescriptor, OpenFlags};
|
||||||
use memfs::Ramfs;
|
use memfs::Ramfs;
|
||||||
use vfs::{Filesystem, Ioctx};
|
use vfs::{Filesystem, Ioctx};
|
||||||
|
@ -6,8 +6,11 @@ use alloc::collections::BTreeMap;
|
|||||||
use libsys::proc::Pid;
|
use libsys::proc::Pid;
|
||||||
|
|
||||||
pub mod elf;
|
pub mod elf;
|
||||||
|
pub mod thread;
|
||||||
|
pub use thread::{Thread, ThreadRef, State as ThreadState};
|
||||||
|
pub(self) use thread::Context;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub use process::{Process, ProcessRef, State as ProcessState};
|
pub use process::{Process, ProcessRef, ProcessState};
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub use io::ProcessIo;
|
pub use io::ProcessIo;
|
||||||
|
|
||||||
@ -52,6 +55,9 @@ pub fn process(id: Pid) -> ProcessRef {
|
|||||||
pub(self) static PROCESSES: IrqSafeSpinLock<BTreeMap<Pid, ProcessRef>> =
|
pub(self) static PROCESSES: IrqSafeSpinLock<BTreeMap<Pid, ProcessRef>> =
|
||||||
IrqSafeSpinLock::new(BTreeMap::new());
|
IrqSafeSpinLock::new(BTreeMap::new());
|
||||||
|
|
||||||
|
pub(self) static THREADS: IrqSafeSpinLock<BTreeMap<u32, ThreadRef>> =
|
||||||
|
IrqSafeSpinLock::new(BTreeMap::new());
|
||||||
|
|
||||||
/// Sets up initial process and enters it.
|
/// Sets up initial process and enters it.
|
||||||
///
|
///
|
||||||
/// See [Scheduler::enter]
|
/// See [Scheduler::enter]
|
||||||
@ -61,6 +67,6 @@ pub(self) static PROCESSES: IrqSafeSpinLock<BTreeMap<Pid, ProcessRef>> =
|
|||||||
/// Unsafe: May only be called once.
|
/// Unsafe: May only be called once.
|
||||||
pub unsafe fn enter() -> ! {
|
pub unsafe fn enter() -> ! {
|
||||||
SCHED.init();
|
SCHED.init();
|
||||||
SCHED.enqueue(Process::new_kernel(init::init_fn, 0).unwrap().id());
|
Process::new_kernel(init::init_fn, 0).unwrap().enqueue();
|
||||||
SCHED.enter();
|
SCHED.enter();
|
||||||
}
|
}
|
||||||
|
@ -5,50 +5,45 @@ use crate::mem::{
|
|||||||
phys::{self, PageUsage},
|
phys::{self, PageUsage},
|
||||||
virt::{MapAttributes, Space},
|
virt::{MapAttributes, Space},
|
||||||
};
|
};
|
||||||
use crate::proc::{wait::Wait, ProcessIo, PROCESSES, SCHED};
|
use crate::proc::{
|
||||||
use crate::sync::IrqSafeSpinLock;
|
wait::Wait, Context, ProcessIo, Thread, ThreadRef, ThreadState, PROCESSES, SCHED, THREADS,
|
||||||
use alloc::rc::Rc;
|
};
|
||||||
|
use crate::sync::{IrqSafeSpinLock, IrqSafeSpinLockGuard};
|
||||||
|
use alloc::{rc::Rc, vec::Vec};
|
||||||
use core::cell::UnsafeCell;
|
use core::cell::UnsafeCell;
|
||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
use libsys::{error::Errno, signal::Signal, proc::{ExitCode, Pid}};
|
use libsys::{
|
||||||
|
error::Errno,
|
||||||
pub use crate::arch::platform::context::{self, Context};
|
proc::{ExitCode, Pid},
|
||||||
|
signal::Signal,
|
||||||
|
};
|
||||||
|
|
||||||
/// Wrapper type for a process struct reference
|
/// Wrapper type for a process struct reference
|
||||||
pub type ProcessRef = Rc<Process>;
|
pub type ProcessRef = Rc<Process>;
|
||||||
|
|
||||||
/// List of possible process states
|
/// List of possible process states
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum State {
|
pub enum ProcessState {
|
||||||
/// Process is ready to be executed and/or is scheduled for it
|
/// Process is alive
|
||||||
Ready,
|
Active,
|
||||||
/// Process is currently running or is in system call/interrupt handler
|
|
||||||
Running,
|
|
||||||
/// Process has finished execution and is waiting to be reaped
|
/// Process has finished execution and is waiting to be reaped
|
||||||
Finished,
|
Finished,
|
||||||
/// Process is waiting for some external event
|
|
||||||
Waiting,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProcessInner {
|
struct ProcessInner {
|
||||||
space: Option<&'static mut Space>,
|
space: Option<&'static mut Space>,
|
||||||
state: State,
|
state: ProcessState,
|
||||||
id: Pid,
|
id: Pid,
|
||||||
wait_flag: bool,
|
|
||||||
exit: Option<ExitCode>,
|
exit: Option<ExitCode>,
|
||||||
signal_entry: usize,
|
threads: Vec<u32>,
|
||||||
signal_stack: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Structure describing an operating system process
|
/// Structure describing an operating system process
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Process {
|
pub struct Process {
|
||||||
ctx: UnsafeCell<Context>,
|
|
||||||
signal_ctx: UnsafeCell<Context>,
|
|
||||||
inner: IrqSafeSpinLock<ProcessInner>,
|
inner: IrqSafeSpinLock<ProcessInner>,
|
||||||
exit_wait: Wait,
|
exit_wait: Wait,
|
||||||
signal_state: AtomicU32,
|
signal_state: AtomicU32,
|
||||||
signal_pending: AtomicU32,
|
|
||||||
/// Process I/O context
|
/// Process I/O context
|
||||||
pub io: IrqSafeSpinLock<ProcessIo>,
|
pub io: IrqSafeSpinLock<ProcessIo>,
|
||||||
}
|
}
|
||||||
@ -57,9 +52,53 @@ impl Process {
|
|||||||
const USTACK_VIRT_TOP: usize = 0x100000000;
|
const USTACK_VIRT_TOP: usize = 0x100000000;
|
||||||
const USTACK_PAGES: usize = 4;
|
const USTACK_PAGES: usize = 4;
|
||||||
|
|
||||||
/// Returns currently executing process
|
#[inline]
|
||||||
|
pub fn id(&self) -> Pid {
|
||||||
|
self.inner.lock().id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn current() -> ProcessRef {
|
pub fn current() -> ProcessRef {
|
||||||
SCHED.current_process()
|
Thread::current().owner().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn manipulate_space<F>(&self, f: F) -> Result<(), Errno>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Space) -> Result<(), Errno>,
|
||||||
|
{
|
||||||
|
f(self.inner.lock().space.as_mut().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<ProcessRef, Errno> {
|
||||||
|
let id = new_kernel_pid();
|
||||||
|
let thread = Thread::new_kernel(Some(id), entry, arg)?;
|
||||||
|
let mut inner = ProcessInner {
|
||||||
|
threads: Vec::new(),
|
||||||
|
id,
|
||||||
|
exit: None,
|
||||||
|
space: None,
|
||||||
|
state: ProcessState::Active,
|
||||||
|
};
|
||||||
|
inner.threads.push(thread.id());
|
||||||
|
|
||||||
|
let res = Rc::new(Self {
|
||||||
|
exit_wait: Wait::new(),
|
||||||
|
io: IrqSafeSpinLock::new(ProcessIo::new()),
|
||||||
|
signal_state: AtomicU32::new(0),
|
||||||
|
inner: IrqSafeSpinLock::new(inner),
|
||||||
|
});
|
||||||
|
debugln!("New kernel process: {:?}", id);
|
||||||
|
let prev = PROCESSES.lock().insert(id, res.clone());
|
||||||
|
assert!(prev.is_none());
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enqueue(&self) {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
for &tid in inner.threads.iter() {
|
||||||
|
SCHED.enqueue(tid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns process (if any) to which `pid` refers
|
/// Returns process (if any) to which `pid` refers
|
||||||
@ -69,201 +108,55 @@ impl Process {
|
|||||||
|
|
||||||
/// Sets a pending signal for a process
|
/// Sets a pending signal for a process
|
||||||
pub fn set_signal(&self, signal: Signal) {
|
pub fn set_signal(&self, signal: Signal) {
|
||||||
let lock = self.inner.lock();
|
let mut lock = self.inner.lock();
|
||||||
|
let main_thread = Thread::get(lock.threads[0]).unwrap();
|
||||||
|
|
||||||
match lock.state {
|
// TODO check that `signal` is not a fault signal
|
||||||
State::Running => {
|
// it is illegal to call this function with
|
||||||
drop(lock);
|
// fault signals
|
||||||
self.enter_signal(signal);
|
|
||||||
|
match main_thread.state() {
|
||||||
|
ThreadState::Running => {
|
||||||
|
Process::enter_signal_on(lock, main_thread, signal);
|
||||||
}
|
}
|
||||||
State::Waiting => {
|
ThreadState::Waiting => {
|
||||||
// TODO abort whatever the process is waiting for
|
// TODO abort whatever the process is waiting for
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
State::Ready => {
|
ThreadState::Ready => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
State::Finished => {
|
ThreadState::Finished => {
|
||||||
// TODO report error back
|
// TODO report error back
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Switches current thread back from signal handler
|
fn enter_signal_on(mut inner: IrqSafeSpinLockGuard<ProcessInner>, thread: ThreadRef, signal: Signal) {
|
||||||
pub fn return_from_signal(&self) {
|
let ttbr0 =
|
||||||
if self.signal_pending.load(Ordering::Acquire) == 0 {
|
inner.space.as_mut().unwrap().address_phys() | ((inner.id.asid() as usize) << 48);
|
||||||
panic!("TODO handle cases when returning from no signal");
|
drop(inner);
|
||||||
}
|
thread.enter_signal(signal, ttbr0);
|
||||||
self.signal_pending.store(0, Ordering::Release);
|
|
||||||
|
|
||||||
let src_ctx = self.signal_ctx.get();
|
|
||||||
let dst_ctx = self.ctx.get();
|
|
||||||
|
|
||||||
assert_eq!(self.inner.lock().state, State::Running);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
(&mut *src_ctx).switch(&mut *dst_ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Switches current thread to a signal handler
|
pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
|
||||||
pub fn enter_signal(&self, signal: Signal) {
|
let lock = self.inner.lock();
|
||||||
if self
|
Process::enter_signal_on(lock, thread, signal);
|
||||||
.signal_pending
|
}
|
||||||
.compare_exchange_weak(0, signal as u32, Ordering::SeqCst, Ordering::Relaxed)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
panic!("Already handling a signal (maybe handle this case)");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
pub fn new_user_thread(&self, entry: usize, stack: usize, arg: usize) -> Result<u32, Errno> {
|
||||||
let mut lock = self.inner.lock();
|
let mut lock = self.inner.lock();
|
||||||
let signal_ctx = unsafe { &mut *self.signal_ctx.get() };
|
|
||||||
|
|
||||||
let dst_id = lock.id;
|
let space_phys = lock.space.as_mut().unwrap().address_phys();
|
||||||
let dst_space_phys = lock.space.as_mut().unwrap().address_phys();
|
let ttbr0 = space_phys | ((lock.id.asid() as usize) << 48);
|
||||||
let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
|
|
||||||
|
|
||||||
debugln!(
|
let thread = Thread::new_user(lock.id, entry, stack, arg, ttbr0)?;
|
||||||
"Signal entry: pc={:#x}, sp={:#x}, ttbr0={:#x}",
|
let tid = thread.id();
|
||||||
lock.signal_entry,
|
lock.threads.push(tid);
|
||||||
lock.signal_stack,
|
SCHED.enqueue(tid);
|
||||||
dst_ttbr0
|
|
||||||
);
|
|
||||||
assert_eq!(lock.state, State::Running);
|
|
||||||
|
|
||||||
unsafe {
|
Ok(tid)
|
||||||
signal_ctx.setup_signal_entry(
|
|
||||||
lock.signal_entry,
|
|
||||||
signal as usize,
|
|
||||||
dst_ttbr0,
|
|
||||||
lock.signal_stack,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let src_ctx = self.ctx.get();
|
|
||||||
drop(lock);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
(&mut *src_ctx).switch(signal_ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets up values needed for signal entry
|
|
||||||
pub fn setup_signal_context(&self, entry: usize, stack: usize) {
|
|
||||||
let mut lock = self.inner.lock();
|
|
||||||
lock.signal_entry = entry;
|
|
||||||
lock.signal_stack = stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Schedules an initial thread for execution
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Unsafe: only allowed to be called once, repeated calls
|
|
||||||
/// will generate undefined behavior
|
|
||||||
pub unsafe fn enter(proc: ProcessRef) -> ! {
|
|
||||||
// FIXME use some global lock to guarantee atomicity of thread entry?
|
|
||||||
proc.inner.lock().state = State::Running;
|
|
||||||
proc.current_context().enter()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Executes a function allowing mutation of the process address space
|
|
||||||
#[inline]
|
|
||||||
pub fn manipulate_space<F: FnOnce(&mut Space) -> Result<(), Errno>>(
|
|
||||||
&self,
|
|
||||||
f: F,
|
|
||||||
) -> Result<(), Errno> {
|
|
||||||
f(self.inner.lock().space.as_mut().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::mut_from_ref)]
|
|
||||||
fn current_context(&self) -> &mut Context {
|
|
||||||
if self.signal_pending.load(Ordering::Acquire) != 0 {
|
|
||||||
unsafe { &mut *self.signal_ctx.get() }
|
|
||||||
} else {
|
|
||||||
unsafe { &mut *self.ctx.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Schedules a next thread for execution
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Unsafe:
|
|
||||||
///
|
|
||||||
/// * Does not ensure src and dst threads are not the same thread
|
|
||||||
/// * Does not ensure src is actually current context
|
|
||||||
pub unsafe fn switch(src: ProcessRef, dst: ProcessRef, discard: bool) {
|
|
||||||
{
|
|
||||||
let mut src_lock = src.inner.lock();
|
|
||||||
let mut dst_lock = dst.inner.lock();
|
|
||||||
|
|
||||||
if !discard {
|
|
||||||
assert_eq!(src_lock.state, State::Running);
|
|
||||||
src_lock.state = State::Ready;
|
|
||||||
}
|
|
||||||
assert!(dst_lock.state == State::Ready || dst_lock.state == State::Waiting);
|
|
||||||
dst_lock.state = State::Running;
|
|
||||||
}
|
|
||||||
|
|
||||||
let src_ctx = src.current_context();
|
|
||||||
let dst_ctx = dst.current_context();
|
|
||||||
|
|
||||||
(&mut *src_ctx).switch(&mut *dst_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Suspends current process with a "waiting" status
|
|
||||||
pub fn enter_wait(&self) {
|
|
||||||
let drop = {
|
|
||||||
let mut lock = self.inner.lock();
|
|
||||||
let drop = lock.state == State::Running;
|
|
||||||
lock.state = State::Waiting;
|
|
||||||
SCHED.dequeue(lock.id);
|
|
||||||
drop
|
|
||||||
};
|
|
||||||
if drop {
|
|
||||||
SCHED.switch(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Changes process wait condition status
|
|
||||||
pub fn set_wait_flag(&self, v: bool) {
|
|
||||||
self.inner.lock().wait_flag = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if process wait condition has not been reached
|
|
||||||
pub fn wait_flag(&self) -> bool {
|
|
||||||
self.inner.lock().wait_flag
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the process ID
|
|
||||||
pub fn id(&self) -> Pid {
|
|
||||||
self.inner.lock().id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new kernel process
|
|
||||||
pub fn new_kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<ProcessRef, Errno> {
|
|
||||||
let id = new_kernel_pid();
|
|
||||||
let res = Rc::new(Self {
|
|
||||||
ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)),
|
|
||||||
signal_ctx: UnsafeCell::new(Context::empty()),
|
|
||||||
io: IrqSafeSpinLock::new(ProcessIo::new()),
|
|
||||||
exit_wait: Wait::new(),
|
|
||||||
signal_state: AtomicU32::new(0),
|
|
||||||
signal_pending: AtomicU32::new(0),
|
|
||||||
inner: IrqSafeSpinLock::new(ProcessInner {
|
|
||||||
signal_entry: 0,
|
|
||||||
signal_stack: 0,
|
|
||||||
id,
|
|
||||||
exit: None,
|
|
||||||
space: None,
|
|
||||||
wait_flag: false,
|
|
||||||
state: State::Ready,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
debugln!("New kernel process: {:?}", id);
|
|
||||||
assert!(PROCESSES.lock().insert(id, res.clone()).is_none());
|
|
||||||
Ok(res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a "fork" of the process, cloning its address space and
|
/// Creates a "fork" of the process, cloning its address space and
|
||||||
@ -277,63 +170,73 @@ impl Process {
|
|||||||
let dst_space_phys = (dst_space as *mut _ as usize) - mem::KERNEL_OFFSET;
|
let dst_space_phys = (dst_space as *mut _ as usize) - mem::KERNEL_OFFSET;
|
||||||
let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
|
let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
|
||||||
|
|
||||||
|
let mut threads = Vec::new();
|
||||||
|
let tid = Thread::fork(Some(dst_id), frame, dst_ttbr0)?.id();
|
||||||
|
threads.push(tid);
|
||||||
|
|
||||||
let dst = Rc::new(Self {
|
let dst = Rc::new(Self {
|
||||||
ctx: UnsafeCell::new(Context::fork(frame, dst_ttbr0)),
|
|
||||||
signal_ctx: UnsafeCell::new(Context::empty()),
|
|
||||||
io: IrqSafeSpinLock::new(src_io.fork()?),
|
|
||||||
exit_wait: Wait::new(),
|
exit_wait: Wait::new(),
|
||||||
|
io: IrqSafeSpinLock::new(src_io.fork()?),
|
||||||
signal_state: AtomicU32::new(0),
|
signal_state: AtomicU32::new(0),
|
||||||
signal_pending: AtomicU32::new(0),
|
|
||||||
inner: IrqSafeSpinLock::new(ProcessInner {
|
inner: IrqSafeSpinLock::new(ProcessInner {
|
||||||
signal_entry: 0,
|
threads,
|
||||||
signal_stack: 0,
|
|
||||||
id: dst_id,
|
|
||||||
exit: None,
|
exit: None,
|
||||||
space: Some(dst_space),
|
space: Some(dst_space),
|
||||||
state: State::Ready,
|
state: ProcessState::Active,
|
||||||
wait_flag: false,
|
id: dst_id,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
debugln!("Process {:?} forked into {:?}", src_inner.id, dst_id);
|
debugln!("Process {:?} forked into {:?}", src_inner.id, dst_id);
|
||||||
assert!(PROCESSES.lock().insert(dst_id, dst).is_none());
|
assert!(PROCESSES.lock().insert(dst_id, dst).is_none());
|
||||||
SCHED.enqueue(dst_id);
|
|
||||||
|
SCHED.enqueue(tid);
|
||||||
|
|
||||||
Ok(dst_id)
|
Ok(dst_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO a way to terminate a single thread?
|
||||||
/// Terminates a process.
|
/// Terminates a process.
|
||||||
pub fn exit<I: Into<ExitCode>>(&self, status: I) {
|
pub fn exit<I: Into<ExitCode>>(status: I) {
|
||||||
let status = status.into();
|
unsafe {
|
||||||
let drop = {
|
asm!("msr daifclr, #0xF");
|
||||||
let mut lock = self.inner.lock();
|
|
||||||
let drop = lock.state == State::Running;
|
|
||||||
infoln!("Process {:?} is exiting: {:?}", lock.id, status);
|
|
||||||
assert!(lock.exit.is_none());
|
|
||||||
lock.exit = Some(status);
|
|
||||||
lock.state = State::Finished;
|
|
||||||
|
|
||||||
if let Some(space) = lock.space.take() {
|
|
||||||
unsafe {
|
|
||||||
Space::release(space);
|
|
||||||
asm!("tlbi aside1, {}", in(reg) ((lock.id.asid() as usize) << 48));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.io.lock().handle_exit();
|
|
||||||
|
|
||||||
SCHED.dequeue(lock.id);
|
|
||||||
drop
|
|
||||||
};
|
|
||||||
self.exit_wait.wakeup_all();
|
|
||||||
if drop {
|
|
||||||
SCHED.switch(true);
|
|
||||||
panic!("This code should never run");
|
|
||||||
}
|
}
|
||||||
|
let status = status.into();
|
||||||
|
let thread = Thread::current();
|
||||||
|
let process = thread.owner().unwrap();
|
||||||
|
let mut lock = process.inner.lock();
|
||||||
|
|
||||||
|
infoln!("Process {:?} is exiting: {:?}", lock.id, status);
|
||||||
|
assert!(lock.exit.is_none());
|
||||||
|
lock.exit = Some(status);
|
||||||
|
lock.state = ProcessState::Finished;
|
||||||
|
|
||||||
|
for &tid in lock.threads.iter() {
|
||||||
|
debugln!("Dequeue {:?}", tid);
|
||||||
|
Thread::get(tid).unwrap().terminate();
|
||||||
|
SCHED.dequeue(tid);
|
||||||
|
}
|
||||||
|
SCHED.debug();
|
||||||
|
|
||||||
|
if let Some(space) = lock.space.take() {
|
||||||
|
unsafe {
|
||||||
|
Space::release(space);
|
||||||
|
asm!("tlbi aside1, {}", in(reg) ((lock.id.asid() as usize) << 48));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.io.lock().handle_exit();
|
||||||
|
|
||||||
|
drop(lock);
|
||||||
|
|
||||||
|
process.exit_wait.wakeup_all();
|
||||||
|
SCHED.switch(true);
|
||||||
|
panic!("This code should never run");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect(&self) -> Option<ExitCode> {
|
fn collect(&self) -> Option<ExitCode> {
|
||||||
let lock = self.inner.lock();
|
let lock = self.inner.lock();
|
||||||
if lock.state == State::Finished {
|
if lock.state == ProcessState::Finished {
|
||||||
lock.exit
|
lock.exit
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -370,33 +273,36 @@ impl Process {
|
|||||||
asm!("msr daifset, #2");
|
asm!("msr daifset, #2");
|
||||||
}
|
}
|
||||||
|
|
||||||
let proc = SCHED.current_process();
|
let proc = Process::current();
|
||||||
let mut lock = proc.inner.lock();
|
let mut process_lock = proc.inner.lock();
|
||||||
if lock.id.is_kernel() {
|
|
||||||
let mut proc_lock = PROCESSES.lock();
|
if process_lock.threads.len() != 1 {
|
||||||
let old_pid = lock.id;
|
todo!();
|
||||||
assert!(
|
}
|
||||||
proc_lock.remove(&old_pid).is_some(),
|
|
||||||
"Failed to downgrade kernel process (remove kernel pid)"
|
let thread = Thread::get(process_lock.threads[0]).unwrap();
|
||||||
);
|
|
||||||
lock.id = new_user_pid();
|
if process_lock.id.is_kernel() {
|
||||||
debugln!(
|
let mut processes = PROCESSES.lock();
|
||||||
"Process downgrades from kernel to user: {:?} -> {:?}",
|
let old_pid = process_lock.id;
|
||||||
old_pid,
|
let new_pid = new_user_pid();
|
||||||
lock.id
|
debugln!("Downgrading process {:?} -> {:?}", old_pid, new_pid);
|
||||||
);
|
|
||||||
assert!(proc_lock.insert(lock.id, proc.clone()).is_none());
|
let r = processes.remove(&old_pid);
|
||||||
unsafe {
|
assert!(r.is_some());
|
||||||
SCHED.hack_current_pid(lock.id);
|
process_lock.id = new_pid;
|
||||||
}
|
let r = processes.insert(new_pid, proc.clone());
|
||||||
|
assert!(r.is_none());
|
||||||
} else {
|
} else {
|
||||||
// Invalidate user ASID
|
// Invalidate user ASID
|
||||||
let input = (lock.id.asid() as usize) << 48;
|
let input = (process_lock.id.asid() as usize) << 48;
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("tlbi aside1, {}", in(reg) input);
|
asm!("tlbi aside1, {}", in(reg) input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread.set_owner(process_lock.id);
|
||||||
|
|
||||||
proc.io.lock().handle_cloexec();
|
proc.io.lock().handle_cloexec();
|
||||||
|
|
||||||
let new_space = Space::alloc_empty()?;
|
let new_space = Space::alloc_empty()?;
|
||||||
@ -419,22 +325,20 @@ impl Process {
|
|||||||
|
|
||||||
debugln!("Will now enter at {:#x}", entry);
|
debugln!("Will now enter at {:#x}", entry);
|
||||||
// TODO drop old address space
|
// TODO drop old address space
|
||||||
lock.space = Some(new_space);
|
process_lock.space = Some(new_space);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// TODO drop old context
|
// TODO drop old context
|
||||||
let ctx = proc.ctx.get();
|
let ctx = thread.ctx.get();
|
||||||
|
|
||||||
ctx.write(Context::user(
|
ctx.write(Context::user(
|
||||||
entry,
|
entry,
|
||||||
arg,
|
arg,
|
||||||
new_space_phys | ((lock.id.asid() as usize) << 48),
|
new_space_phys | ((process_lock.id.asid() as usize) << 48),
|
||||||
Self::USTACK_VIRT_TOP,
|
Self::USTACK_VIRT_TOP,
|
||||||
));
|
));
|
||||||
|
|
||||||
assert_eq!(lock.state, State::Running);
|
drop(process_lock);
|
||||||
|
|
||||||
drop(lock);
|
|
||||||
|
|
||||||
(*ctx).enter();
|
(*ctx).enter();
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
//!
|
//!
|
||||||
use crate::proc::{Pid, Process, ProcessRef, PROCESSES};
|
use crate::proc::{Pid, Process, ProcessRef, Thread, ThreadRef, PROCESSES, THREADS};
|
||||||
use crate::sync::IrqSafeSpinLock;
|
use crate::sync::IrqSafeSpinLock;
|
||||||
use crate::util::InitOnce;
|
use crate::util::InitOnce;
|
||||||
use alloc::{collections::VecDeque, rc::Rc};
|
use alloc::{collections::VecDeque, rc::Rc};
|
||||||
|
|
||||||
struct SchedulerInner {
|
struct SchedulerInner {
|
||||||
queue: VecDeque<Pid>,
|
queue: VecDeque<u32>,
|
||||||
idle: Option<Pid>,
|
idle: Option<u32>,
|
||||||
current: Option<Pid>,
|
current: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process scheduler state and queues
|
/// Process scheduler state and queues
|
||||||
@ -23,7 +23,7 @@ impl SchedulerInner {
|
|||||||
current: None,
|
current: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.idle = Some(Process::new_kernel(idle_fn, 0).unwrap().id());
|
this.idle = Some(Thread::new_kernel(None, idle_fn, 0).unwrap().id());
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
@ -39,13 +39,21 @@ impl Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Schedules a thread for execution
|
/// Schedules a thread for execution
|
||||||
pub fn enqueue(&self, pid: Pid) {
|
pub fn enqueue(&self, tid: u32) {
|
||||||
self.inner.get().lock().queue.push_back(pid);
|
self.inner.get().lock().queue.push_back(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes given `pid` from execution queue
|
/// Removes given `tid` from execution queue
|
||||||
pub fn dequeue(&self, pid: Pid) {
|
pub fn dequeue(&self, tid: u32) {
|
||||||
self.inner.get().lock().queue.retain(|&p| p != pid)
|
self.inner.get().lock().queue.retain(|&p| p != tid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug(&self) {
|
||||||
|
let lock = self.inner.get().lock();
|
||||||
|
debugln!("Scheduler queue:");
|
||||||
|
for &tid in lock.queue.iter() {
|
||||||
|
debugln!("TID: {:?}", tid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs initial process entry.
|
/// Performs initial process entry.
|
||||||
@ -63,11 +71,11 @@ impl Scheduler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inner.current = Some(id);
|
inner.current = Some(id);
|
||||||
PROCESSES.lock().get(&id).unwrap().clone()
|
THREADS.lock().get(&id).unwrap().clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
asm!("msr daifset, #2");
|
asm!("msr daifset, #2");
|
||||||
Process::enter(thread)
|
Thread::enter(thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This hack is required to be called from execve() when downgrading current
|
/// This hack is required to be called from execve() when downgrading current
|
||||||
@ -76,8 +84,14 @@ impl Scheduler {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Unsafe: only allowed to be called from Process::execve()
|
/// Unsafe: only allowed to be called from Process::execve()
|
||||||
pub unsafe fn hack_current_pid(&self, new: Pid) {
|
pub unsafe fn hack_current_tid(&self, old: u32, new: u32) {
|
||||||
self.inner.get().lock().current = Some(new);
|
let mut lock = self.inner.get().lock();
|
||||||
|
match lock.current {
|
||||||
|
Some(t) if t == old => {
|
||||||
|
lock.current = Some(new);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Switches to the next task scheduled for execution. If there're
|
/// Switches to the next task scheduled for execution. If there're
|
||||||
@ -87,7 +101,7 @@ impl Scheduler {
|
|||||||
let mut inner = self.inner.get().lock();
|
let mut inner = self.inner.get().lock();
|
||||||
let current = inner.current.unwrap();
|
let current = inner.current.unwrap();
|
||||||
|
|
||||||
if !discard && current != Pid::IDLE {
|
if !discard && current != 0 {
|
||||||
// Put the process into the back of the queue
|
// Put the process into the back of the queue
|
||||||
inner.queue.push_back(current);
|
inner.queue.push_back(current);
|
||||||
}
|
}
|
||||||
@ -100,7 +114,7 @@ impl Scheduler {
|
|||||||
|
|
||||||
inner.current = Some(next);
|
inner.current = Some(next);
|
||||||
let (from, to) = {
|
let (from, to) = {
|
||||||
let lock = PROCESSES.lock();
|
let lock = THREADS.lock();
|
||||||
(
|
(
|
||||||
lock.get(¤t).unwrap().clone(),
|
lock.get(¤t).unwrap().clone(),
|
||||||
lock.get(&next).unwrap().clone(),
|
lock.get(&next).unwrap().clone(),
|
||||||
@ -113,17 +127,23 @@ impl Scheduler {
|
|||||||
if !Rc::ptr_eq(&from, &to) {
|
if !Rc::ptr_eq(&from, &to) {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("msr daifset, #2");
|
asm!("msr daifset, #2");
|
||||||
Process::switch(from, to, discard);
|
Thread::switch(from, to, discard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a Rc-reference to currently running process
|
pub fn current_thread(&self) -> ThreadRef {
|
||||||
pub fn current_process(&self) -> ProcessRef {
|
|
||||||
let inner = self.inner.get().lock();
|
let inner = self.inner.get().lock();
|
||||||
let current = inner.current.unwrap();
|
let id = inner.current.unwrap();
|
||||||
PROCESSES.lock().get(¤t).unwrap().clone()
|
THREADS.lock().get(&id).unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /// Returns a Rc-reference to currently running process
|
||||||
|
// pub fn current_process(&self) -> ProcessRef {
|
||||||
|
// let inner = self.inner.get().lock();
|
||||||
|
// let current = inner.current.unwrap();
|
||||||
|
// PROCESSES.lock().get(¤t).unwrap().clone()
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the scheduler has been initialized
|
/// Returns `true` if the scheduler has been initialized
|
||||||
|
325
kernel/src/proc/thread.rs
Normal file
325
kernel/src/proc/thread.rs
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
use crate::arch::aarch64::exception::ExceptionFrame;
|
||||||
|
use crate::proc::{wait::Wait, Process, ProcessRef, SCHED, THREADS};
|
||||||
|
use crate::sync::IrqSafeSpinLock;
|
||||||
|
use alloc::{rc::Rc, vec::Vec};
|
||||||
|
use core::cell::UnsafeCell;
|
||||||
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
use libsys::{error::Errno, proc::Pid, signal::Signal};
|
||||||
|
|
||||||
|
pub use crate::arch::platform::context::{self, Context};
|
||||||
|
|
||||||
|
pub type ThreadRef = Rc<Thread>;
|
||||||
|
|
||||||
|
/// List of possible process states
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum State {
|
||||||
|
/// Process is ready to be executed and/or is scheduled for it
|
||||||
|
Ready,
|
||||||
|
/// Process is currently running or is in system call/interrupt handler
|
||||||
|
Running,
|
||||||
|
/// Process has finished execution and is waiting to be reaped
|
||||||
|
Finished,
|
||||||
|
/// Process is waiting for some external event
|
||||||
|
Waiting,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ThreadInner {
|
||||||
|
id: u32,
|
||||||
|
state: State,
|
||||||
|
owner: Option<Pid>,
|
||||||
|
pending_wait: Option<&'static Wait>,
|
||||||
|
wait_flag: bool,
|
||||||
|
signal_entry: usize,
|
||||||
|
signal_stack: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Thread {
|
||||||
|
inner: IrqSafeSpinLock<ThreadInner>,
|
||||||
|
pub(super) ctx: UnsafeCell<Context>,
|
||||||
|
signal_ctx: UnsafeCell<Context>,
|
||||||
|
signal_pending: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Thread {
|
||||||
|
#[inline]
|
||||||
|
pub fn current() -> ThreadRef {
|
||||||
|
SCHED.current_thread()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get(tid: u32) -> Option<ThreadRef> {
|
||||||
|
THREADS.lock().get(&tid).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn owner(&self) -> Option<ProcessRef> {
|
||||||
|
self.inner.lock().owner.and_then(Process::get)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new kernel process
|
||||||
|
pub fn new_kernel(
|
||||||
|
owner: Option<Pid>,
|
||||||
|
entry: extern "C" fn(usize) -> !,
|
||||||
|
arg: usize,
|
||||||
|
) -> Result<ThreadRef, Errno> {
|
||||||
|
let id = new_tid();
|
||||||
|
|
||||||
|
let res = Rc::new(Self {
|
||||||
|
ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)),
|
||||||
|
signal_ctx: UnsafeCell::new(Context::empty()),
|
||||||
|
signal_pending: AtomicU32::new(0),
|
||||||
|
inner: IrqSafeSpinLock::new(ThreadInner {
|
||||||
|
signal_entry: 0,
|
||||||
|
signal_stack: 0,
|
||||||
|
id,
|
||||||
|
owner,
|
||||||
|
pending_wait: None,
|
||||||
|
wait_flag: false,
|
||||||
|
state: State::Ready,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
debugln!("New kernel thread: {:?}", id);
|
||||||
|
assert!(THREADS.lock().insert(id, res.clone()).is_none());
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new userspace process
|
||||||
|
pub fn new_user(
|
||||||
|
owner: Pid,
|
||||||
|
entry: usize,
|
||||||
|
stack: usize,
|
||||||
|
arg: usize,
|
||||||
|
ttbr0: usize,
|
||||||
|
) -> Result<ThreadRef, Errno> {
|
||||||
|
let id = new_tid();
|
||||||
|
|
||||||
|
let res = Rc::new(Self {
|
||||||
|
ctx: UnsafeCell::new(Context::user(entry, arg, ttbr0, stack)),
|
||||||
|
signal_ctx: UnsafeCell::new(Context::empty()),
|
||||||
|
signal_pending: AtomicU32::new(0),
|
||||||
|
inner: IrqSafeSpinLock::new(ThreadInner {
|
||||||
|
signal_entry: 0,
|
||||||
|
signal_stack: 0,
|
||||||
|
id,
|
||||||
|
owner: Some(owner),
|
||||||
|
pending_wait: None,
|
||||||
|
wait_flag: false,
|
||||||
|
state: State::Ready,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
debugln!("New userspace thread: {:?}", id);
|
||||||
|
assert!(THREADS.lock().insert(id, res.clone()).is_none());
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fork(
|
||||||
|
owner: Option<Pid>,
|
||||||
|
frame: &ExceptionFrame,
|
||||||
|
ttbr0: usize,
|
||||||
|
) -> Result<ThreadRef, Errno> {
|
||||||
|
let id = new_tid();
|
||||||
|
|
||||||
|
let res = Rc::new(Self {
|
||||||
|
ctx: UnsafeCell::new(Context::fork(frame, ttbr0)),
|
||||||
|
signal_ctx: UnsafeCell::new(Context::empty()),
|
||||||
|
signal_pending: AtomicU32::new(0),
|
||||||
|
inner: IrqSafeSpinLock::new(ThreadInner {
|
||||||
|
signal_entry: 0,
|
||||||
|
signal_stack: 0,
|
||||||
|
id,
|
||||||
|
owner,
|
||||||
|
pending_wait: None,
|
||||||
|
wait_flag: false,
|
||||||
|
state: State::Ready,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
debugln!("Forked new user thread: {:?}", id);
|
||||||
|
assert!(THREADS.lock().insert(id, res.clone()).is_none());
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn id(&self) -> u32 {
|
||||||
|
self.inner.lock().id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Schedules an initial thread for execution
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Unsafe: only allowed to be called once, repeated calls
|
||||||
|
/// will generate undefined behavior
|
||||||
|
pub unsafe fn enter(thread: ThreadRef) -> ! {
|
||||||
|
// FIXME use some global lock to guarantee atomicity of thread entry?
|
||||||
|
thread.inner.lock().state = State::Running;
|
||||||
|
thread.current_context().enter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Schedules a next thread for execution
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Unsafe:
|
||||||
|
///
|
||||||
|
/// * Does not ensure src and dst threads are not the same thread
|
||||||
|
/// * Does not ensure src is actually current context
|
||||||
|
pub unsafe fn switch(src: ThreadRef, dst: ThreadRef, discard: bool) {
|
||||||
|
{
|
||||||
|
let mut src_lock = src.inner.lock();
|
||||||
|
let mut dst_lock = dst.inner.lock();
|
||||||
|
|
||||||
|
if !discard {
|
||||||
|
assert_eq!(src_lock.state, State::Running);
|
||||||
|
src_lock.state = State::Ready;
|
||||||
|
}
|
||||||
|
assert!(dst_lock.state == State::Ready || dst_lock.state == State::Waiting);
|
||||||
|
dst_lock.state = State::Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
let src_ctx = src.current_context();
|
||||||
|
let dst_ctx = dst.current_context();
|
||||||
|
|
||||||
|
(&mut *src_ctx).switch(&mut *dst_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::mut_from_ref)]
|
||||||
|
fn current_context(&self) -> &mut Context {
|
||||||
|
if self.signal_pending.load(Ordering::Acquire) != 0 {
|
||||||
|
unsafe { &mut *self.signal_ctx.get() }
|
||||||
|
} else {
|
||||||
|
unsafe { &mut *self.ctx.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Suspends current process with a "waiting" status
|
||||||
|
pub fn enter_wait(&self) {
|
||||||
|
let drop = {
|
||||||
|
let mut lock = self.inner.lock();
|
||||||
|
let drop = lock.state == State::Running;
|
||||||
|
lock.state = State::Waiting;
|
||||||
|
SCHED.dequeue(lock.id);
|
||||||
|
drop
|
||||||
|
};
|
||||||
|
if drop {
|
||||||
|
SCHED.switch(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Changes process wait condition status
|
||||||
|
pub fn setup_wait(&self, wait: *const Wait) {
|
||||||
|
let mut lock = self.inner.lock();
|
||||||
|
// FIXME this is not cool
|
||||||
|
lock.pending_wait = Some(unsafe { &*wait });
|
||||||
|
lock.wait_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_wait_reached(&self) {
|
||||||
|
let mut lock = self.inner.lock();
|
||||||
|
lock.wait_flag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_wait(&self) {
|
||||||
|
let mut lock = self.inner.lock();
|
||||||
|
lock.pending_wait = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if process wait condition has not been reached
|
||||||
|
pub fn wait_flag(&self) -> bool {
|
||||||
|
self.inner.lock().wait_flag
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Switches current thread back from signal handler
|
||||||
|
pub fn return_from_signal(&self) {
|
||||||
|
if self.signal_pending.load(Ordering::Acquire) == 0 {
|
||||||
|
panic!("TODO handle cases when returning from no signal");
|
||||||
|
}
|
||||||
|
self.signal_pending.store(0, Ordering::Release);
|
||||||
|
|
||||||
|
let src_ctx = self.signal_ctx.get();
|
||||||
|
let dst_ctx = self.ctx.get();
|
||||||
|
|
||||||
|
assert_eq!(self.inner.lock().state, State::Running);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(&mut *src_ctx).switch(&mut *dst_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn state(&self) -> State {
|
||||||
|
self.inner.lock().state
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_owner(&self, pid: Pid) {
|
||||||
|
self.inner.lock().owner = Some(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up values needed for signal entry
|
||||||
|
pub fn set_signal_entry(&self, entry: usize, stack: usize) {
|
||||||
|
let mut lock = self.inner.lock();
|
||||||
|
lock.signal_entry = entry;
|
||||||
|
lock.signal_stack = stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Switches process main thread to a signal handler
|
||||||
|
pub fn enter_signal(&self, signal: Signal, ttbr0: usize) {
|
||||||
|
if self
|
||||||
|
.signal_pending
|
||||||
|
.compare_exchange_weak(0, signal as u32, Ordering::SeqCst, Ordering::Relaxed)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
panic!("Already handling a signal (maybe handle this case)");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut lock = self.inner.lock();
|
||||||
|
if lock.signal_entry == 0 || lock.signal_stack == 0 {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal_ctx = unsafe { &mut *self.signal_ctx.get() };
|
||||||
|
let src_ctx = self.ctx.get();
|
||||||
|
|
||||||
|
debugln!(
|
||||||
|
"Signal entry: tid={}, pc={:#x}, sp={:#x}, ttbr0={:#x}",
|
||||||
|
lock.id,
|
||||||
|
lock.signal_entry,
|
||||||
|
lock.signal_stack,
|
||||||
|
ttbr0
|
||||||
|
);
|
||||||
|
assert_eq!(lock.state, State::Running);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
signal_ctx.setup_signal_entry(lock.signal_entry, signal as usize, ttbr0, lock.signal_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(lock);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(&mut *src_ctx).switch(signal_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn terminate(&self) {
|
||||||
|
let mut lock = self.inner.lock();
|
||||||
|
lock.state = State::Finished;
|
||||||
|
let tid = lock.id;
|
||||||
|
let wait = lock.pending_wait.take();
|
||||||
|
drop(lock);
|
||||||
|
if let Some(wait) = wait {
|
||||||
|
wait.abort(tid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Thread {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
debugln!("Dropping process {:?}", self.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_tid() -> u32 {
|
||||||
|
static LAST: AtomicU32 = AtomicU32::new(1);
|
||||||
|
let id = LAST.fetch_add(1, Ordering::Relaxed);
|
||||||
|
assert!(id < 256, "Out of user TIDs");
|
||||||
|
id
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::arch::machine;
|
use crate::arch::machine;
|
||||||
use crate::dev::timer::TimestampSource;
|
use crate::dev::timer::TimestampSource;
|
||||||
use crate::proc::{self, sched::SCHED, Process, ProcessRef};
|
use crate::proc::{self, sched::SCHED, Process, Thread, ThreadRef};
|
||||||
use crate::sync::IrqSafeSpinLock;
|
use crate::sync::IrqSafeSpinLock;
|
||||||
use alloc::collections::LinkedList;
|
use alloc::collections::LinkedList;
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
@ -11,11 +11,11 @@ use libsys::{error::Errno, stat::FdSet, proc::Pid};
|
|||||||
/// Wait channel structure. Contains a queue of processes
|
/// Wait channel structure. Contains a queue of processes
|
||||||
/// waiting for some event to happen.
|
/// waiting for some event to happen.
|
||||||
pub struct Wait {
|
pub struct Wait {
|
||||||
queue: IrqSafeSpinLock<LinkedList<Pid>>,
|
queue: IrqSafeSpinLock<LinkedList<u32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Timeout {
|
struct Timeout {
|
||||||
pid: Pid,
|
tid: u32,
|
||||||
deadline: Duration,
|
deadline: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,9 +30,9 @@ pub fn tick() {
|
|||||||
|
|
||||||
while let Some(item) = cursor.current() {
|
while let Some(item) = cursor.current() {
|
||||||
if time > item.deadline {
|
if time > item.deadline {
|
||||||
let pid = item.pid;
|
let tid = item.tid;
|
||||||
cursor.remove_current();
|
cursor.remove_current();
|
||||||
SCHED.enqueue(pid);
|
SCHED.enqueue(tid);
|
||||||
} else {
|
} else {
|
||||||
cursor.move_next();
|
cursor.move_next();
|
||||||
}
|
}
|
||||||
@ -56,50 +56,51 @@ pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(
|
pub fn select(
|
||||||
proc: ProcessRef,
|
thread: ThreadRef,
|
||||||
mut rfds: Option<&mut FdSet>,
|
mut rfds: Option<&mut FdSet>,
|
||||||
mut wfds: Option<&mut FdSet>,
|
mut wfds: Option<&mut FdSet>,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> Result<usize, Errno> {
|
) -> Result<usize, Errno> {
|
||||||
// TODO support wfds
|
todo!();
|
||||||
if wfds.is_some() || rfds.is_none() {
|
// // TODO support wfds
|
||||||
todo!();
|
// if wfds.is_some() || rfds.is_none() {
|
||||||
}
|
// todo!();
|
||||||
let read = rfds.as_deref().map(FdSet::clone);
|
// }
|
||||||
let write = wfds.as_deref().map(FdSet::clone);
|
// let read = rfds.as_deref().map(FdSet::clone);
|
||||||
rfds.as_deref_mut().map(FdSet::reset);
|
// let write = wfds.as_deref().map(FdSet::clone);
|
||||||
wfds.as_deref_mut().map(FdSet::reset);
|
// rfds.as_deref_mut().map(FdSet::reset);
|
||||||
|
// wfds.as_deref_mut().map(FdSet::reset);
|
||||||
|
|
||||||
let deadline = timeout.map(|v| v + machine::local_timer().timestamp().unwrap());
|
// let deadline = timeout.map(|v| v + machine::local_timer().timestamp().unwrap());
|
||||||
let mut io = proc.io.lock();
|
// let mut io = proc.io.lock();
|
||||||
|
|
||||||
loop {
|
// loop {
|
||||||
if let Some(read) = &read {
|
// if let Some(read) = &read {
|
||||||
for fd in read.iter() {
|
// for fd in read.iter() {
|
||||||
let file = io.file(fd)?;
|
// let file = io.file(fd)?;
|
||||||
if file.borrow().is_ready(false)? {
|
// if file.borrow().is_ready(false)? {
|
||||||
rfds.as_mut().unwrap().set(fd);
|
// rfds.as_mut().unwrap().set(fd);
|
||||||
return Ok(1);
|
// return Ok(1);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if let Some(write) = &write {
|
// if let Some(write) = &write {
|
||||||
for fd in write.iter() {
|
// for fd in write.iter() {
|
||||||
let file = io.file(fd)?;
|
// let file = io.file(fd)?;
|
||||||
if file.borrow().is_ready(true)? {
|
// if file.borrow().is_ready(true)? {
|
||||||
wfds.as_mut().unwrap().set(fd);
|
// wfds.as_mut().unwrap().set(fd);
|
||||||
return Ok(1);
|
// return Ok(1);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Suspend
|
// // Suspend
|
||||||
match WAIT_SELECT.wait(deadline) {
|
// match WAIT_SELECT.wait(deadline) {
|
||||||
Err(Errno::TimedOut) => return Ok(0),
|
// Err(Errno::TimedOut) => return Ok(0),
|
||||||
Err(e) => return Err(e),
|
// Err(e) => return Err(e),
|
||||||
Ok(_) => {}
|
// Ok(_) => {}
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wait {
|
impl Wait {
|
||||||
@ -115,12 +116,12 @@ impl Wait {
|
|||||||
let mut queue = self.queue.lock();
|
let mut queue = self.queue.lock();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
while limit != 0 && !queue.is_empty() {
|
while limit != 0 && !queue.is_empty() {
|
||||||
let pid = queue.pop_front();
|
let tid = queue.pop_front();
|
||||||
if let Some(pid) = pid {
|
if let Some(tid) = tid {
|
||||||
let mut tick_lock = TICK_LIST.lock();
|
let mut tick_lock = TICK_LIST.lock();
|
||||||
let mut cursor = tick_lock.cursor_front_mut();
|
let mut cursor = tick_lock.cursor_front_mut();
|
||||||
while let Some(item) = cursor.current() {
|
while let Some(item) = cursor.current() {
|
||||||
if pid == item.pid {
|
if tid == item.tid {
|
||||||
cursor.remove_current();
|
cursor.remove_current();
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -129,8 +130,8 @@ impl Wait {
|
|||||||
}
|
}
|
||||||
drop(tick_lock);
|
drop(tick_lock);
|
||||||
|
|
||||||
proc::process(pid).set_wait_flag(false);
|
Thread::get(tid).unwrap().set_wait_reached();
|
||||||
SCHED.enqueue(pid);
|
SCHED.enqueue(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
limit -= 1;
|
limit -= 1;
|
||||||
@ -149,29 +150,54 @@ impl Wait {
|
|||||||
self.wakeup_some(1);
|
self.wakeup_some(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn abort(&self, tid: u32) {
|
||||||
|
let mut queue = self.queue.lock();
|
||||||
|
let mut tick_lock = TICK_LIST.lock();
|
||||||
|
let mut cursor = tick_lock.cursor_front_mut();
|
||||||
|
while let Some(item) = cursor.current() {
|
||||||
|
if tid == item.tid {
|
||||||
|
cursor.remove_current();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
cursor.move_next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cursor = queue.cursor_front_mut();
|
||||||
|
while let Some(item) = cursor.current() {
|
||||||
|
if tid == *item {
|
||||||
|
cursor.remove_current();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
cursor.move_next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Suspends current process until event is signalled or
|
/// Suspends current process until event is signalled or
|
||||||
/// (optional) deadline is reached
|
/// (optional) deadline is reached
|
||||||
pub fn wait(&self, deadline: Option<Duration>) -> Result<(), Errno> {
|
pub fn wait(&self, deadline: Option<Duration>) -> Result<(), Errno> {
|
||||||
let proc = Process::current();
|
let thread = Thread::current();
|
||||||
//let deadline = timeout.map(|t| machine::local_timer().timestamp().unwrap() + t);
|
//let deadline = timeout.map(|t| machine::local_timer().timestamp().unwrap() + t);
|
||||||
let mut queue_lock = self.queue.lock();
|
let mut queue_lock = self.queue.lock();
|
||||||
|
|
||||||
queue_lock.push_back(proc.id());
|
queue_lock.push_back(thread.id());
|
||||||
proc.set_wait_flag(true);
|
thread.setup_wait(self);
|
||||||
|
|
||||||
if let Some(deadline) = deadline {
|
if let Some(deadline) = deadline {
|
||||||
TICK_LIST.lock().push_back(Timeout {
|
TICK_LIST.lock().push_back(Timeout {
|
||||||
pid: proc.id(),
|
tid: thread.id(),
|
||||||
deadline,
|
deadline,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if !proc.wait_flag() {
|
if !thread.wait_flag() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(queue_lock);
|
drop(queue_lock);
|
||||||
proc.enter_wait();
|
thread.enter_wait();
|
||||||
queue_lock = self.queue.lock();
|
queue_lock = self.queue.lock();
|
||||||
|
|
||||||
if let Some(deadline) = deadline {
|
if let Some(deadline) = deadline {
|
||||||
@ -179,7 +205,7 @@ impl Wait {
|
|||||||
let mut cursor = queue_lock.cursor_front_mut();
|
let mut cursor = queue_lock.cursor_front_mut();
|
||||||
|
|
||||||
while let Some(&mut item) = cursor.current() {
|
while let Some(&mut item) = cursor.current() {
|
||||||
if proc.id() == item {
|
if thread.id() == item {
|
||||||
cursor.remove_current();
|
cursor.remove_current();
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::arch::platform::exception::ExceptionFrame;
|
use crate::arch::platform::exception::ExceptionFrame;
|
||||||
use crate::debug::Level;
|
use crate::debug::Level;
|
||||||
use crate::proc::{elf, wait, Process, ProcessIo};
|
use crate::proc::{self, elf, wait, Process, ProcessIo, Thread};
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use core::ops::DerefMut;
|
use core::ops::DerefMut;
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
@ -55,7 +55,7 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
|||||||
match num {
|
match num {
|
||||||
// Process management system calls
|
// Process management system calls
|
||||||
abi::SYS_EXIT => {
|
abi::SYS_EXIT => {
|
||||||
Process::current().exit(args[0] as i32);
|
Process::exit(args[0] as i32);
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,13 +174,11 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
|||||||
res.map(|_| 0)
|
res.map(|_| 0)
|
||||||
}
|
}
|
||||||
abi::SYS_EX_SIGNAL => {
|
abi::SYS_EX_SIGNAL => {
|
||||||
let proc = Process::current();
|
Thread::current().set_signal_entry(args[0], args[1]);
|
||||||
proc.setup_signal_context(args[0], args[1]);
|
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
abi::SYS_EX_SIGRETURN => {
|
abi::SYS_EX_SIGRETURN => {
|
||||||
let proc = Process::current();
|
Thread::current().return_from_signal();
|
||||||
proc.return_from_signal();
|
|
||||||
panic!("This code won't run");
|
panic!("This code won't run");
|
||||||
}
|
}
|
||||||
abi::SYS_EX_KILL => {
|
abi::SYS_EX_KILL => {
|
||||||
@ -196,6 +194,19 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
|||||||
};
|
};
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
abi::SYS_EX_CLONE => {
|
||||||
|
let entry = args[0];
|
||||||
|
let stack = args[1];
|
||||||
|
let arg = args[2];
|
||||||
|
|
||||||
|
Process::current()
|
||||||
|
.new_user_thread(entry, stack, arg)
|
||||||
|
.map(|e| e as usize)
|
||||||
|
}
|
||||||
|
abi::SYS_EX_YIELD => {
|
||||||
|
proc::switch();
|
||||||
|
Ok(0)
|
||||||
|
},
|
||||||
|
|
||||||
abi::SYS_SELECT => {
|
abi::SYS_SELECT => {
|
||||||
let rfds = validate_user_ptr_struct_option::<FdSet>(args[0])?;
|
let rfds = validate_user_ptr_struct_option::<FdSet>(args[0])?;
|
||||||
@ -206,15 +217,15 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
|||||||
Some(Duration::from_nanos(args[2] as u64))
|
Some(Duration::from_nanos(args[2] as u64))
|
||||||
};
|
};
|
||||||
|
|
||||||
let proc = Process::current();
|
wait::select(Thread::current(), rfds, wfds, timeout)
|
||||||
wait::select(proc, rfds, wfds, timeout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let proc = Process::current();
|
let thread = Thread::current();
|
||||||
|
let proc = thread.owner().unwrap();
|
||||||
errorln!("Undefined system call: {}", num);
|
errorln!("Undefined system call: {}", num);
|
||||||
proc.enter_signal(Signal::InvalidSystemCall);
|
proc.enter_fault_signal(thread, Signal::InvalidSystemCall);
|
||||||
todo!()
|
Err(Errno::InvalidArgument)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ pub const SYS_EX_NANOSLEEP: usize = 129;
|
|||||||
pub const SYS_EX_SIGNAL: usize = 130;
|
pub const SYS_EX_SIGNAL: usize = 130;
|
||||||
pub const SYS_EX_SIGRETURN: usize = 131;
|
pub const SYS_EX_SIGRETURN: usize = 131;
|
||||||
pub const SYS_EX_KILL: usize = 132;
|
pub const SYS_EX_KILL: usize = 132;
|
||||||
|
pub const SYS_EX_CLONE: usize = 133;
|
||||||
|
pub const SYS_EX_YIELD: usize = 134;
|
||||||
|
|
||||||
pub const SYS_EXIT: usize = 1;
|
pub const SYS_EXIT: usize = 1;
|
||||||
pub const SYS_READ: usize = 2;
|
pub const SYS_READ: usize = 2;
|
||||||
|
@ -271,6 +271,32 @@ pub fn sys_ex_kill(pid: SignalDestination, signum: Signal) -> Result<(), Errno>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn sys_ex_clone(entry: usize, stack: usize, arg: usize) -> Result<usize, Errno> {
|
||||||
|
Errno::from_syscall(unsafe {
|
||||||
|
syscall!(
|
||||||
|
abi::SYS_EX_CLONE,
|
||||||
|
argn!(entry),
|
||||||
|
argn!(stack),
|
||||||
|
argn!(arg)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn sys_ex_yield() {
|
||||||
|
unsafe {
|
||||||
|
syscall!(abi::SYS_EX_YIELD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn sys_ex_undefined() {
|
||||||
|
unsafe {
|
||||||
|
syscall!(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn sys_select(
|
pub fn sys_select(
|
||||||
read_fds: Option<&mut FdSet>,
|
read_fds: Option<&mut FdSet>,
|
||||||
|
@ -47,6 +47,6 @@ impl Errno {
|
|||||||
|
|
||||||
impl From<usize> for Errno {
|
impl From<usize> for Errno {
|
||||||
fn from(u: usize) -> Errno {
|
fn from(u: usize) -> Errno {
|
||||||
todo!()
|
unsafe { core::mem::transmute(u as u32) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,16 @@ pub mod os;
|
|||||||
pub mod sys;
|
pub mod sys;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
|
|
||||||
|
use sys::Signal;
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
extern "C" fn _signal_handler(arg: sys::Signal) -> ! {
|
extern "C" fn _signal_handler(arg: Signal) -> ! {
|
||||||
trace!("Entered signal handler: arg={:?}", arg);
|
trace!("Entered signal handler: arg={:?}", arg);
|
||||||
|
match arg {
|
||||||
|
Signal::Interrupt | Signal::SegmentationFault =>
|
||||||
|
loop {},
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
sys::sys_ex_sigreturn();
|
sys::sys_ex_sigreturn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub use libsys::signal::{Signal, SignalDestination};
|
pub use libsys::signal::{Signal, SignalDestination};
|
||||||
|
pub use libsys::proc::ExitCode;
|
||||||
pub use libsys::termios;
|
pub use libsys::termios;
|
||||||
pub use libsys::calls::*;
|
pub use libsys::calls::*;
|
||||||
pub use libsys::stat::{self, FileDescriptor};
|
pub use libsys::stat::{self, FileDescriptor};
|
||||||
@ -28,7 +29,7 @@ impl RawMutex {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn lock(&self) {
|
pub unsafe fn lock(&self) {
|
||||||
while !self.try_lock() {
|
while !self.try_lock() {
|
||||||
asm!("nop");
|
sys_ex_yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,3 +15,4 @@ path = "src/shell/main.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libusr = { path = "../libusr" }
|
libusr = { path = "../libusr" }
|
||||||
|
lazy_static = { version = "*", features = ["spin_no_std"] }
|
||||||
|
@ -3,24 +3,67 @@
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate libusr;
|
extern crate libusr;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
use libusr::io::{self, Read};
|
use libusr::io::{self, Read};
|
||||||
|
use libusr::sys::{Signal, SignalDestination};
|
||||||
|
use libusr::sync::Mutex;
|
||||||
|
|
||||||
|
static mut THREAD_STACK: [u8; 8192] = [0; 8192];
|
||||||
|
static mut THREAD_SIGNAL_STACK: [u8; 8192] = [0; 8192];
|
||||||
|
lazy_static! {
|
||||||
|
static ref MUTEX: Mutex<()> = Mutex::new(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sleep(ns: u64) {
|
||||||
|
let mut rem = [0; 2];
|
||||||
|
libusr::sys::sys_ex_nanosleep(ns, &mut rem).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn0_signal(arg: Signal) {
|
||||||
|
trace!("fn0_signal");
|
||||||
|
unsafe {
|
||||||
|
libusr::sys::sys_exit(libusr::sys::ExitCode::from(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn0(_arg: usize) {
|
||||||
|
unsafe {
|
||||||
|
libusr::sys::sys_ex_signal(fn0_signal as usize, THREAD_SIGNAL_STACK.as_mut_ptr().add(8192) as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::ptr::read_volatile(0x1234 as *const u32);
|
||||||
|
}
|
||||||
|
loop {}
|
||||||
|
//loop {
|
||||||
|
// sleep(100_000_000);
|
||||||
|
// println!("Tick from B");
|
||||||
|
// {
|
||||||
|
// let lock = MUTEX.lock();
|
||||||
|
// sleep(1_000_000_000);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_fault() {
|
||||||
|
unsafe {
|
||||||
|
core::ptr::read_volatile(0x1238 as *const u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn main() -> i32 {
|
fn main() -> i32 {
|
||||||
let mut buf = [0; 512];
|
unsafe {
|
||||||
let mut stdin = io::stdin();
|
libusr::sys::sys_ex_clone(fn0 as usize, THREAD_STACK.as_mut_ptr().add(8192) as usize, 0);
|
||||||
|
|
||||||
eprintln!("stderr test");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let count = stdin.read(&mut buf).unwrap();
|
|
||||||
if count == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let line = core::str::from_utf8(&buf[..count]).unwrap();
|
|
||||||
println!("{:?}", line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sleep(1_000_000_000);
|
||||||
|
|
||||||
|
do_fault();
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user