#![allow(unused)]
use core::mem;
use core::ptr;
use core::ops::Deref;
use crate::sched;
use crate::syscall;
use crate::time;
use crate::stack::Stack;
use crate::sched::event::Event;
use core::ptr::NonNull;
use bern_arch::arch::memory_protection::{MemoryRegion, Size};
use bern_arch::arch::Arch;
use bern_arch::memory_protection::{Config, Type, Access, Permission};
use bern_arch::IMemoryProtection;
use bern_conf::CONF;
#[derive(Copy, Clone)]
#[repr(u8)]
pub enum Transition {
None,
Sleeping,
Blocked,
Resuming,
Terminating,
}
pub type RunnableResult = ();
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Priority(pub u8);
impl Into<usize> for Priority {
fn into(self) -> usize {
self.0 as usize
}
}
pub struct TaskBuilder {
stack: Option<Stack>,
priority: Priority,
}
impl TaskBuilder {
pub fn static_stack(&mut self, stack: Stack) -> &mut Self {
self.stack = Some(stack);
self
}
pub fn priority(&mut self, priority: Priority) -> &mut Self {
if priority.0 >= CONF.task.priorities as u8 {
panic!("Priority out of range. todo: check at compile time");
} else if priority.0 == CONF.task.priorities as u8 - 1 {
panic!("Priority reserved for idle task. Use `is_idle_task()` instead. todo: check at compile time")
}
self.priority = priority;
self
}
pub fn idle_task(&mut self) -> &mut Self {
self.priority = Priority(CONF.task.priorities as u8 - 1);
self
}
pub fn spawn<F>(&mut self, closure: F)
where F: 'static + FnMut() -> RunnableResult
{
syscall::move_closure_to_stack(closure, self);
let mut stack = match self.stack.as_mut() {
Some(stack) => stack,
None => panic!("todo: allocate stack"),
};
let mut runnable: &mut (dyn FnMut() -> RunnableResult);
unsafe {
runnable = &mut *(stack.ptr as *mut F);
}
syscall::task_spawn(self, &runnable);
}
pub(crate) fn move_closure_to_stack(&mut self, closure: *const u8, size_bytes: usize) {
let mut stack = match self.stack.as_mut() {
Some(stack) => stack,
None => panic!("todo: return error"),
};
unsafe {
let mut ptr = stack.ptr as *mut u8;
ptr = ptr.offset(-(size_bytes as isize));
ptr::copy_nonoverlapping(closure, ptr, size_bytes);
stack.ptr = ptr as *mut usize;
}
}
pub(crate) fn build(&mut self, runnable: &&mut (dyn FnMut() -> RunnableResult)) {
let mut stack = match self.stack.as_mut() {
Some(stack) => stack,
None => panic!("todo: return error"),
};
let mut ptr = stack.ptr as *mut u8;
let runnable_len = mem::size_of_val(runnable);
unsafe {
ptr = Self::align_ptr(ptr, 8);
ptr = ptr.offset(-(runnable_len as isize));
ptr::write(ptr as *mut _, runnable.deref());
}
let runnable_ptr = ptr as *mut usize;
unsafe { ptr = Self::align_ptr(ptr, 8); }
stack.ptr = ptr as *mut usize;
let memory_regions = [Arch::prepare_memory_region(
5,
Config {
addr: stack.bottom_ptr() as *const _,
memory: Type::SramInternal,
size: stack.size(),
access: Access { user: Permission::ReadWrite, system: Permission::ReadWrite },
executable: false
}),
Arch::prepare_unused_region(6),
Arch::prepare_unused_region(7)
];
let mut task = Task {
transition: Transition::None,
runnable_ptr,
next_wut: 0,
stack: self.stack.take().unwrap(),
priority: self.priority,
blocking_event: None,
memory_regions,
};
sched::add(task)
}
unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
let offset = ptr as usize % align;
ptr.offset(-(offset as isize))
}
}
pub struct Task {
transition: Transition,
runnable_ptr: *mut usize,
next_wut: u64,
stack: Stack,
priority: Priority,
blocking_event: Option<NonNull<Event>>,
memory_regions: [MemoryRegion; 3],
}
impl Task {
pub fn new() -> TaskBuilder {
TaskBuilder {
stack: None,
priority: Priority(CONF.task.priorities - 2),
}
}
pub(crate) fn runnable_ptr(&self) -> *const usize {
self.runnable_ptr
}
pub(crate) fn stack(&self) -> &Stack {
&self.stack
}
pub(crate) fn stack_mut(&mut self) -> &mut Stack {
&mut self.stack
}
pub(crate) fn stack_ptr(&self) -> *mut usize {
self.stack.ptr
}
pub(crate) fn set_stack_ptr(&mut self, psp: *mut usize) {
self.stack.ptr = psp;
}
pub(crate) fn stack_top(&self) -> *const usize {
self.stack.bottom_ptr() as *const _
}
pub(crate) fn next_wut(&self) -> u64 {
self.next_wut
}
pub(crate) fn sleep(&mut self, ms: u32) {
self.next_wut = time::tick() + u64::from(ms);
}
pub(crate) fn transition(&self) -> &Transition {
&self.transition
}
pub(crate) fn set_transition(&mut self, transition: Transition) {
self.transition = transition;
}
pub(crate) fn priority(&self) -> Priority {
self.priority
}
pub(crate) fn memory_regions(&self) -> &[MemoryRegion; 3] {
&self.memory_regions
}
pub(crate) fn blocking_event(&self) -> Option<NonNull<Event>> {
self.blocking_event
}
pub(crate) fn set_blocking_event(&mut self, event: NonNull<Event>) {
self.blocking_event = Some(event);
}
}
pub(crate) fn entry(runnable: &mut &mut (dyn FnMut() -> RunnableResult)) {
(runnable)();
}
#[cfg(all(test, not(target_os = "none")))]
mod tests {
use super::*;
use bern_arch::arch::Arch;
}