extern crate alloc;
extern crate crossbeam_queue;
mod schedule;
mod task_state;
mod tick;
use crate::spinlock::Spinlock;
use crate::syscall;
use crate::task::schedule::{RoundRobin, Scheduler};
use crate::task::task_state::{TaskState, TaskStateStruct};
use crate::task::tick::TickCounter;
use alloc::boxed::Box;
use alloc::collections::LinkedList;
use alloc::sync::Arc;
use alloc::vec;
use alloc::vec::Vec;
use core::mem::size_of;
use core::ptr::null_mut;
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use crossbeam_queue::SegQueue;
use fe_osi::semaphore::Semaphore;
#[repr(C)]
union StackPtr {
reference: &'static usize,
ptr: *const usize,
num: usize,
}
#[repr(C)]
pub(crate) struct Task {
sp: StackPtr,
dynamic_stack: Vec<usize>,
task_info: Option<Box<NewTaskInfo>>,
state: TaskStateStruct,
pub(crate) pid: usize,
}
unsafe impl Send for Task {}
unsafe impl Sync for Task {}
pub(crate) struct NewTaskInfo {
pub ep: *const u32,
pub param: *mut u32,
}
const INIT_XPSR: usize = 0x01000000;
pub const DEFAULT_STACK_SIZE: usize = 1536;
static mut KERNEL_STACK: [usize; DEFAULT_STACK_SIZE] = [0; DEFAULT_STACK_SIZE];
static mut TICKS: TickCounter = TickCounter::new();
static mut PLACEHOLDER_TASK: Task = Task {
sp: StackPtr { num: 0 },
dynamic_stack: Vec::new(),
task_info: None,
state: TaskStateStruct::new(),
pid: 0,
};
static PUSHING_TASK: AtomicBool = AtomicBool::new(false);
static mut SCHEDULER: RoundRobin = RoundRobin::new();
static mut KERNEL_TASK: Option<Arc<Task>> = None;
static mut NEXT_TASK: Option<Arc<Task>> = None;
static mut CUR_TASK: Option<&mut Task> = None;
lazy_static! {
static ref NEW_TASK_QUEUE: SegQueue<Arc<Task>> = SegQueue::new();
}
static mut TRIGGER_CONTEXT_SWITCH: fn() = idle;
extern "C" {
pub fn context_switch();
}
unsafe fn get_cur_task_mut() -> &'static mut Task {
match &mut CUR_TASK {
Some(task) => task,
None => &mut PLACEHOLDER_TASK,
}
}
#[no_mangle]
pub(crate) unsafe extern "C" fn get_cur_task() -> &'static Task {
match &CUR_TASK {
Some(task) => task,
None => &PLACEHOLDER_TASK,
}
}
#[no_mangle]
pub(crate) unsafe extern "C" fn set_cur_task(new_val: &'static mut Task) {
CUR_TASK = Some(new_val);
}
#[no_mangle]
pub(crate) unsafe extern "C" fn get_next_task() -> &'static Task {
match &NEXT_TASK {
Some(task) => task,
None => &PLACEHOLDER_TASK,
}
}
unsafe fn scheduler() {
let default_task = match &KERNEL_TASK {
Some(task) => Arc::clone(&task),
None => panic!("No valid KERNEL_TASK in scheduler"),
};
let count = match &NEXT_TASK {
Some(task) => Arc::strong_count(&task),
None => 0xFF,
};
if count == 1 {
return;
}
match SCHEDULER.next() {
Some(task) => {
NEXT_TASK = Some(task);
}
None => {
NEXT_TASK = Some(Arc::clone(&default_task));
}
}
}
pub(crate) unsafe fn do_context_switch() {
static CS_LOCK: Spinlock = Spinlock::new();
if CS_LOCK.try_take() {
scheduler();
CS_LOCK.give();
TRIGGER_CONTEXT_SWITCH();
}
}
pub unsafe extern "C" fn sys_tick() {
TICKS.inc();
do_context_switch();
}
pub(crate) fn sleep(sleep_ticks: u64) -> bool {
unsafe {
let ret_val = get_cur_task()
.state
.try_set(TaskState::Asleep(TICKS.get() + sleep_ticks));
do_context_switch();
ret_val
}
}
pub(crate) fn block(sem: *const Semaphore) -> bool {
unsafe {
let ret_val = get_cur_task().state.try_set(TaskState::Blocking(sem));
do_context_switch();
ret_val
}
}
unsafe fn set_initial_stack(
stack_ptr: *const usize,
entry_point: *const usize,
param: *mut usize,
) -> *const usize {
let mut cur_ptr = ((stack_ptr as usize) - size_of::<usize>()) as *mut usize;
*cur_ptr = INIT_XPSR;
cur_ptr = (cur_ptr as usize - size_of::<usize>()) as *mut usize;
*cur_ptr = entry_point as usize;
cur_ptr = (cur_ptr as usize - size_of::<usize>()) as *mut usize;
*cur_ptr = idle as usize;
cur_ptr = (cur_ptr as usize - size_of::<usize>()) as *mut usize;
for _i in 0..13 {
*cur_ptr = param as usize;
cur_ptr = (cur_ptr as usize - size_of::<usize>()) as *mut usize;
}
(cur_ptr as usize + size_of::<usize>()) as *const usize
}
fn get_new_pid() -> usize {
static PID: AtomicUsize = AtomicUsize::new(1);
PID.fetch_add(1, Ordering::SeqCst)
}
pub(crate) unsafe fn add_task(
stack_size: usize,
entry_point: *const usize,
param: *mut usize,
) -> Arc<Task> {
let mut new_task = Task {
sp: StackPtr { num: 0 },
dynamic_stack: vec![0; stack_size],
task_info: None,
state: TaskStateStruct::new(),
pid: get_new_pid(),
};
new_task.sp.ptr = &new_task.dynamic_stack[0] as *const usize;
new_task.sp.num += size_of::<usize>() * (stack_size as usize);
new_task.sp.ptr = set_initial_stack(new_task.sp.ptr, entry_point, param);
let task_ref = Arc::new(new_task);
PUSHING_TASK.store(true, Ordering::SeqCst);
NEW_TASK_QUEUE.push(Arc::clone(&task_ref));
PUSHING_TASK.store(false, Ordering::SeqCst);
task_ref
}
pub(crate) unsafe fn add_task_static(
stack_ptr: &'static usize,
stack_size: usize,
entry_point: *const usize,
param: Option<*mut usize>,
) -> Arc<Task> {
let mut new_task = Task {
sp: StackPtr {
reference: stack_ptr,
},
dynamic_stack: Vec::new(),
task_info: None,
state: TaskStateStruct::new(),
pid: get_new_pid(),
};
new_task.sp.num += size_of::<usize>() * (stack_size as usize);
let param_ptr = match param {
Some(p) => p as *mut usize,
None => null_mut(),
};
new_task.sp.ptr = set_initial_stack(new_task.sp.ptr, entry_point, param_ptr);
let task_ref = Arc::new(new_task);
PUSHING_TASK.store(true, Ordering::SeqCst);
NEW_TASK_QUEUE.push(Arc::clone(&task_ref));
PUSHING_TASK.store(false, Ordering::SeqCst);
task_ref
}
pub(crate) fn new_task_helper(task_info: Box<NewTaskInfo>) -> ! {
let task_param: &u32 = unsafe { &*task_info.param };
let task_ep: fn(&u32) = unsafe { core::mem::transmute(task_info.ep) };
let task = unsafe { get_cur_task_mut() };
task.task_info = Some(task_info);
task_ep(task_param);
fe_osi::exit();
loop {}
}
pub(crate) unsafe fn remove_task() -> bool {
let ret_val = get_cur_task().state.try_set(TaskState::Zombie);
do_context_switch();
ret_val
}
pub fn start_scheduler<F: FnOnce(usize)>(
trigger_context_switch: fn(),
enable_systick: F,
reload_val: usize,
) {
syscall::link_syscalls();
unsafe {
let kernel_task_ref = add_task_static(
&KERNEL_STACK[0],
DEFAULT_STACK_SIZE,
kernel as *const usize,
None,
);
KERNEL_TASK = Some(Arc::clone(&kernel_task_ref));
TRIGGER_CONTEXT_SWITCH = trigger_context_switch;
}
enable_systick(reload_val);
loop {}
}
fn kernel(_: &mut u32) {
let mut first_push = true;
let mut task_list: LinkedList<Arc<Task>> = LinkedList::new();
loop {
let mut task_num: usize = 0;
let mut deleted_task_num: usize = 0;
let mut delete_task = false;
if first_push {
unsafe {
super::disable_interrupts();
}
}
while !NEW_TASK_QUEUE.is_empty() && !PUSHING_TASK.load(Ordering::SeqCst) {
match NEW_TASK_QUEUE.pop() {
Ok(new_task) => {
task_list.push_back(Arc::clone(&new_task));
unsafe {
SCHEDULER.add_task(new_task);
}
}
Err(_) => {
break;
}
}
}
if first_push {
first_push = false;
unsafe {
super::enable_interrupts();
}
}
for task in task_list.iter() {
let mut new_state = TaskState::Runnable;
let mut has_new_state = false;
let task_state = task.state.try_get().unwrap_or(TaskState::Runnable);
match task_state {
TaskState::Runnable => {}
TaskState::Asleep(wake_up_ticks) => {
unsafe {
if wake_up_ticks < TICKS.get() {
has_new_state = true;
new_state = TaskState::Runnable;
}
}
}
TaskState::Blocking(sem) => {
let sem_ref: &Semaphore = unsafe { &*sem };
if sem_ref.is_available() {
has_new_state = true;
new_state = TaskState::Runnable;
}
}
TaskState::Zombie => {
delete_task = true;
deleted_task_num = task_num;
}
}
if has_new_state {
task.state.try_set(new_state);
}
task_num += 1;
}
if delete_task {
let removed_task = task_list.remove(deleted_task_num);
match &removed_task.task_info {
Some(info) => {
if !info.param.is_null() {
unsafe {
core::mem::drop(Box::from_raw(info.param));
}
}
}
None => (),
}
unsafe {
crate::fe_alloc::clear_deleted_task(removed_task.pid);
}
}
unsafe {
do_context_switch();
}
}
}
fn idle() {
loop {}
}