+use core::cell::RefCell;
+
+use critical_section::Mutex;
+
+use crate::task::TaskId;
+
+pub const MAX_CAPS: usize = 4;
+
+static CAP_REGISTRY: Mutex<RefCell<Option<CapRegistry>>> = Mutex::new(RefCell::new(None));
+
+#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
+pub enum CapType {
+ ConsoleRead = 0,
+ ConsoleWrite = 1,
+ Led = 2,
+ Keyboard = 3,
+}
+
+pub struct CapSet(u32);
+
+impl CapSet {
+ pub fn new(caps: &[CapType]) -> CapSet {
+ let mut v = 0;
+ for c in caps {
+ v |= 1 << (*c as usize);
+ }
+ CapSet(v)
+ }
+
+ pub fn has(&self, cap: CapType) -> bool {
+ self.0 & (1 << (cap as usize)) != 0
+ }
+
+ pub fn add(&mut self, caps: &[CapType]) -> Result<&mut CapSet, ()> {
+ for c in caps {
+ if self.has(*c) {
+ return Err(());
+ }
+ self.0 |= 1 << (*c as usize);
+ }
+ Ok(self)
+ }
+
+ pub fn remove(&mut self, caps: &[CapType]) -> Result<&mut CapSet, ()> {
+ for c in caps {
+ if !self.has(*c) {
+ return Err(());
+ }
+ self.0 &= !(1 << (*c as usize));
+ }
+ Ok(self)
+ }
+}
+
+#[derive(Debug)]
+pub struct CapToken(CapType);
+impl CapToken {
+ pub fn captype(&self) -> CapType {
+ self.0
+ }
+}
+
+#[derive(Debug)]
+enum CapState {
+ Available(CapToken),
+ Taken(TaskId),
+}
+
+impl CapState {
+ fn available(&self) -> bool {
+ match self {
+ CapState::Available(_) => true,
+ CapState::Taken(_) => false,
+ }
+ }
+
+ fn take(&mut self, tid: TaskId) -> Option<CapToken> {
+ if let CapState::Available(_) = self {
+ let old = core::mem::replace(self, CapState::Taken(tid));
+ let CapState::Available(t) = old else {
+ unreachable!();
+ };
+ Some(t)
+ } else {
+ None
+ }
+ }
+
+ fn replace(&mut self, token: CapToken, tid: TaskId) -> bool {
+ if let CapState::Taken(tid2) = self {
+ if tid == *tid2 {
+ *self = CapState::Available(token);
+ true
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }
+}
+
+#[derive(Debug)]
+struct CapEntry {
+ cap: CapType,
+ state: CapState,
+}
+
+impl CapEntry {
+ fn new(cap: CapType) -> CapEntry {
+ CapEntry {
+ cap,
+ state: CapState::Available(CapToken(cap)),
+ }
+ }
+
+ fn take(&mut self, tid: TaskId) -> Option<CapToken> {
+ self.state.take(tid)
+ }
+
+ fn replace(&mut self, token: CapToken, tid: TaskId) -> bool {
+ self.state.replace(token, tid)
+ }
+}
+
+#[derive(Debug)]
+pub struct CapRegistry {
+ capabilities: heapless::Vec<CapEntry, 10>,
+}
+
+impl CapRegistry {
+ pub fn new() -> CapRegistry {
+ let mut capabilities = heapless::Vec::new();
+ capabilities
+ .push(CapEntry::new(CapType::ConsoleRead))
+ .unwrap();
+ capabilities
+ .push(CapEntry::new(CapType::ConsoleWrite))
+ .unwrap();
+ capabilities.push(CapEntry::new(CapType::Led)).unwrap();
+ capabilities.push(CapEntry::new(CapType::Keyboard)).unwrap();
+ CapRegistry { capabilities }
+ }
+
+ pub fn available(&self, cap: CapType) -> bool {
+ let cap_entry = self.capabilities.iter().find(|ce| ce.cap == cap && ce.state.available());
+ cap_entry.is_some()
+ }
+
+ pub fn take(&mut self, cap: &[CapType], tid: TaskId) -> Option<heapless::Vec<CapToken, MAX_CAPS>> {
+ // first, check if we have the capabilities available
+ let available = cap.iter().all(|cap| self.available(*cap));
+ if !available {
+ return None;
+ }
+
+ // Then take all the tokens
+ cap.iter().map(|c| {
+ let cap_entry = self.capabilities.iter_mut().find(|ce| ce.cap == *c).unwrap();
+ cap_entry.take(tid)
+ }).collect()
+ }
+
+ pub fn replace(&mut self, token: CapToken, tid: TaskId) -> bool {
+ let cap_entry = self.capabilities.iter_mut().find(|ce| ce.cap == token.0);
+ if let Some(cap_entry) = cap_entry {
+ cap_entry.replace(token, tid)
+ } else {
+ false
+ }
+ }
+}
+
+pub fn init_capabilities() {
+ critical_section::with(|cs| {
+ let mut cap_registry = CAP_REGISTRY.borrow_ref_mut(cs);
+ *cap_registry = Some(CapRegistry::new());
+ })
+}
+
+pub fn with_cap_registry<F, R>(mut f: F) -> R
+where
+ F: FnMut(&mut CapRegistry) -> R,
+{
+ critical_section::with(|cs| {
+ let mut cap_registry = CAP_REGISTRY.borrow_ref_mut(cs);
+ if let Some(ref mut cap_registry) = *cap_registry {
+ f(cap_registry)
+ } else {
+ panic!("Capabilities registry not initialized");
+ }
+ })
+}
\ No newline at end of file
use crate::peripherals::with_usb;
#[link_section = ".kgram1"]
-static CONSOLE: Mutex<RefCell<Option<Console>>> = Mutex::new(RefCell::new(None));
+static CONSOLE: Mutex<RefCell<Console>> = Mutex::new(RefCell::new(Console::new()));
pub enum ConsoleError {
Read,
}
impl Console {
- pub fn new() -> Console {
+ const fn new() -> Console {
Console { }
}
}
pub fn init_console() {
- critical_section::with(|cs| {
- let mut console = CONSOLE.borrow_ref_mut(cs);
- *console = Some(Console::new());
- })
+ // Does nothing since console can be statically initialized
}
pub fn write(bytes: &[u8]) {
critical_section::with(|cs| {
let mut console = CONSOLE.borrow_ref_mut(cs);
- if let Some(ref mut console) = *console {
- console.write(bytes).ok();
- }
- // if the console is not initialized, ignore the write
- // TODO: buffer writes until console is initialized?
+ console.write(bytes).ok();
});
}
mod allocator;
mod asm;
+mod capabilities;
mod console;
mod panic;
mod peripherals;
use doa_hallonbrod as bsp;
use allocator::init_allocator;
+use capabilities::{init_capabilities, CapType};
use console::init_console;
use task::{init_tasks, with_task_manager};
init_peripherals();
init_console();
init_tasks();
+ init_capabilities();
let ticks = timer::ticks();
kprintf!("Kernel started: {} ticks\r\n", ticks);
*/
with_task_manager(|tm| {
- tm.add_task(task_one);
- tm.add_task(task_two);
+ tm.add_task(task_one, &[CapType::ConsoleWrite]).expect("launch task one");
+ tm.add_task(task_two, &[CapType::Led]).expect("launch task two");
});
unsafe {
on = !on;
stdlib::sleep(223);
}
-}
\ No newline at end of file
+}
use embedded_hal::digital::v2::OutputPin;
+use crate::capabilities::CapType;
use crate::console;
use crate::peripherals::with_peripherals;
-use crate::task::{with_task_manager, TaskRegisters};
+use crate::task::{with_task_manager, TaskRegisters, current_process_has_capability};
use crate::timer::ticks;
global_asm!(
});
}
2 => {
+ if !current_process_has_capability(CapType::ConsoleWrite) {
+ return;
+ }
+
let bytes = core::slice::from_raw_parts(regs.r1 as *const u8, regs.r2 as usize);
console::write(bytes);
}
3 => {
+ if !current_process_has_capability(CapType::Led) {
+ return;
+ }
+
let on = regs.r1 != 0;
with_peripherals(|p| {
if on {
pub(crate) use entry::{TaskEntry, TaskRegisters};
pub use error::TaskError;
+use crate::capabilities::{with_cap_registry, CapToken, CapType, MAX_CAPS};
use crate::timer::{ticks, Ticks};
#[link_section = ".appram"]
static mut APPRAM: [u8; 0x10000] = [0u8; 0x10000];
-pub type TaskID = u32;
+pub type TaskId = u32;
-static TASK_MANAGER: Mutex<RefCell<Option<TaskManager>>> = Mutex::new(RefCell::new(None));
+static TASK_MANAGER: Mutex<RefCell<TaskManager>> = Mutex::new(RefCell::new(TaskManager::new()));
pub struct TaskManager {
region_map: [Option<u32>; 10],
}
impl TaskManager {
- fn new() -> TaskManager {
+ const fn new() -> TaskManager {
TaskManager {
region_map: [None; 10],
next_tid: 0,
self.active = false;
}
- pub fn add_task(&mut self, entry: fn() -> !) {
+ pub fn add_task(
+ &mut self,
+ entry: fn() -> !,
+ requested_caps: &[CapType],
+ ) -> Result<TaskId, TaskError> {
let tid = self.next_tid;
self.next_tid += 1;
+ let caps: heapless::Vec<CapToken, MAX_CAPS> =
+ with_cap_registry(|cr| cr.take(requested_caps, tid))
+ .ok_or(TaskError::CapabilityUnavailable)?;
+
let region = self.region_map.iter().position(|v| v.is_none());
let Some(region) = region else {
- panic!("no mappable regions!");
+ return Err(TaskError::Allocation);
};
self.region_map[region] = Some(tid);
let entry = entry as *const () as u32;
data_size: data_size as usize,
state: TaskState::Running,
ticks_ran: 0,
+ caps,
};
- self.tasks.push(t).expect("too many tasks");
+ self.tasks.push(t).map_err(|_| TaskError::TooManyTasks)?;
+
+ Ok(tid)
}
pub(crate) fn schedule(&mut self, now: Ticks, regs: &mut TaskRegisters) {
let t1 = ticks() + t;
self.tasks[self.current_task].state = TaskState::Sleeping(t1);
}
+
+ pub fn current_process_has_capability(&self, cap: CapType) -> bool {
+ let task = &self.tasks[self.current_task];
+ task.caps.iter().any(|token| token.captype() == cap)
+ }
}
pub fn init_tasks() {
- critical_section::with(|cs| {
- let mut task_manager = TASK_MANAGER.borrow_ref_mut(cs);
- *task_manager = Some(TaskManager::new());
- });
+ // Does nothing since TaskManager can be statically initialized
}
pub fn with_task_manager<F, R>(mut f: F) -> R
{
critical_section::with(|cs| {
let mut task_manager = TASK_MANAGER.borrow_ref_mut(cs);
- if let Some(ref mut task_manager) = *task_manager {
- return f(task_manager);
- } else {
- panic!("USB Manager not initialized");
- }
+ f(&mut task_manager)
})
}
+pub fn current_process_has_capability(cap: CapType) -> bool {
+ with_task_manager(|tm| tm.current_process_has_capability(cap))
+}
+
extern "C" {
fn _launch(regs: &TaskRegisters) -> !;
}
pub unsafe fn launch(entry: fn() -> !) -> ! {
let regs = with_task_manager(|tm| {
- tm.add_task(entry);
+ tm.add_task(entry, &[]).ok();
tm.start();
tm.tasks[0].task_registers.clone()
});
-use crate::task::TaskID;
+use crate::capabilities::{CapToken, MAX_CAPS};
+use crate::task::TaskId;
use crate::timer::Ticks;
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub(crate) struct TaskEntry {
- pub id: TaskID,
+ pub id: TaskId,
pub task_registers: TaskRegisters,
pub data: u32,
pub data_size: usize,
pub state: TaskState,
pub ticks_ran: u32,
+ pub caps: heapless::Vec<CapToken, MAX_CAPS>,
}
#[derive(Debug)]
pub enum TaskError {
Allocation,
+ CapabilityUnavailable,
+ TooManyTasks,
}
impl fmt::Display for TaskError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TaskError::Allocation => write!(f, "allocation"),
+ TaskError::CapabilityUnavailable => write!(f, "capability unavailable"),
+ TaskError::TooManyTasks => write!(f, "too many tasks"),
}
}
}