158 lines
5.7 KiB
Rust
158 lines
5.7 KiB
Rust
use core::sync::atomic::Ordering;
|
|
|
|
use context::{arch, contexts, Context, Status, CONTEXT_ID};
|
|
use context::signal::signal_handler;
|
|
use gdt;
|
|
use interrupt;
|
|
use interrupt::irq::PIT_TICKS;
|
|
use time;
|
|
|
|
/// Switch to the next context
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Do not call this while holding locks!
|
|
pub unsafe fn switch() -> bool {
|
|
use core::ops::DerefMut;
|
|
|
|
//set PIT Interrupt counter to 0, giving each process same amount of PIT ticks
|
|
PIT_TICKS.store(0, Ordering::SeqCst);
|
|
|
|
// Set the global lock to avoid the unsafe operations below from causing issues
|
|
while arch::CONTEXT_SWITCH_LOCK.compare_and_swap(false, true, Ordering::SeqCst) {
|
|
interrupt::pause();
|
|
}
|
|
|
|
let cpu_id = ::cpu_id();
|
|
|
|
let from_ptr;
|
|
let mut to_ptr = 0 as *mut Context;
|
|
let mut to_sig = None;
|
|
{
|
|
let contexts = contexts();
|
|
{
|
|
let context_lock = contexts
|
|
.current()
|
|
.expect("context::switch: not inside of context");
|
|
let mut context = context_lock.write();
|
|
from_ptr = context.deref_mut() as *mut Context;
|
|
}
|
|
|
|
let check_context = |context: &mut Context| -> bool {
|
|
// Take ownership if not already owned
|
|
if context.cpu_id == None {
|
|
context.cpu_id = Some(cpu_id);
|
|
// println!("{}: take {} {}", cpu_id, context.id, ::core::str::from_utf8_unchecked(&context.name.lock()));
|
|
}
|
|
|
|
// Restore from signal
|
|
if context.ksig_restore {
|
|
let ksig = context.ksig.take().expect("context::switch: ksig not set with ksig_restore");
|
|
context.arch = ksig.0;
|
|
if let Some(ref mut kfx) = context.kfx {
|
|
kfx.clone_from_slice(&ksig.1.expect("context::switch: ksig kfx not set with ksig_restore"));
|
|
} else {
|
|
panic!("context::switch: kfx not set with ksig_restore");
|
|
}
|
|
if let Some(ref mut kstack) = context.kstack {
|
|
kstack.clone_from_slice(&ksig.2.expect("context::switch: ksig kstack not set with ksig_restore"));
|
|
} else {
|
|
panic!("context::switch: kstack not set with ksig_restore");
|
|
}
|
|
context.ksig_restore = false;
|
|
|
|
context.unblock();
|
|
}
|
|
|
|
// Unblock when there are pending signals
|
|
if context.status == Status::Blocked && !context.pending.is_empty() {
|
|
context.unblock();
|
|
}
|
|
|
|
// Wake from sleep
|
|
if context.status == Status::Blocked && context.wake.is_some() {
|
|
let wake = context.wake.expect("context::switch: wake not set");
|
|
|
|
let current = time::monotonic();
|
|
if current.0 > wake.0 || (current.0 == wake.0 && current.1 >= wake.1) {
|
|
context.wake = None;
|
|
context.unblock();
|
|
}
|
|
}
|
|
|
|
// Switch to context if it needs to run, is not currently running, and is owned by the current CPU
|
|
!context.running && context.status == Status::Runnable && context.cpu_id == Some(cpu_id)
|
|
};
|
|
|
|
for (pid, context_lock) in contexts.iter() {
|
|
if *pid > (*from_ptr).id {
|
|
let mut context = context_lock.write();
|
|
if check_context(&mut context) {
|
|
to_ptr = context.deref_mut() as *mut Context;
|
|
if (&mut *to_ptr).ksig.is_none() {
|
|
to_sig = context.pending.pop_front();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if to_ptr as usize == 0 {
|
|
for (pid, context_lock) in contexts.iter() {
|
|
if *pid < (*from_ptr).id {
|
|
let mut context = context_lock.write();
|
|
if check_context(&mut context) {
|
|
to_ptr = context.deref_mut() as *mut Context;
|
|
if (&mut *to_ptr).ksig.is_none() {
|
|
to_sig = context.pending.pop_front();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Switch process states, TSS stack pointer, and store new context ID
|
|
if to_ptr as usize != 0 {
|
|
(&mut *from_ptr).running = false;
|
|
(&mut *to_ptr).running = true;
|
|
if let Some(ref stack) = (*to_ptr).kstack {
|
|
if cfg!(feature = "pti") {
|
|
use arch::x86_64::pti::{PTI_CPU_STACK, PTI_CONTEXT_STACK};
|
|
gdt::TSS.rsp[0] = (PTI_CPU_STACK.as_ptr() as usize + PTI_CPU_STACK.len()) as u64;
|
|
PTI_CONTEXT_STACK = stack.as_ptr() as usize + stack.len();
|
|
} else {
|
|
gdt::TSS.rsp[0] = (stack.as_ptr() as usize + stack.len()) as u64;
|
|
}
|
|
}
|
|
CONTEXT_ID.store((&mut *to_ptr).id, Ordering::SeqCst);
|
|
}
|
|
|
|
// Unset global lock before switch, as arch is only usable by the current CPU at this time
|
|
arch::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
|
|
|
|
if to_ptr as usize == 0 {
|
|
// No target was found, return
|
|
|
|
false
|
|
} else {
|
|
if let Some(sig) = to_sig {
|
|
// Signal was found, run signal handler
|
|
|
|
//TODO: Allow nested signals
|
|
assert!((&mut *to_ptr).ksig.is_none());
|
|
|
|
let arch = (&mut *to_ptr).arch.clone();
|
|
let kfx = (&mut *to_ptr).kfx.clone();
|
|
let kstack = (&mut *to_ptr).kstack.clone();
|
|
(&mut *to_ptr).ksig = Some((arch, kfx, kstack));
|
|
(&mut *to_ptr).arch.signal_stack(signal_handler, sig);
|
|
}
|
|
|
|
(&mut *from_ptr).arch.switch_to(&mut (&mut *to_ptr).arch);
|
|
|
|
true
|
|
}
|
|
}
|