commit:7cfe784d88145dfa2768498ff805848088d28b91
author:Chip
committer:Chip
date:Sat Sep 2 23:37:49 2023 -0500
parents:3b240d6a16e08b9ea76a1ad25f9e152cb7d321cd
Add capabilities
diff --git a/src/capabilities.rs b/src/capabilities.rs
line changes: +193/-0
index 0000000..fdf5ad0
--- /dev/null
+++ b/src/capabilities.rs
@@ -0,0 +1,193 @@
+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

diff --git a/src/console.rs b/src/console.rs
line changes: +4/-11
index 6c2d1a7..ae17d8e
--- a/src/console.rs
+++ b/src/console.rs
@@ -6,7 +6,7 @@ use critical_section::Mutex;
 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,
@@ -17,7 +17,7 @@ struct Console {
 }
 
 impl Console {
-    pub fn new() -> Console {
+    const fn new() -> Console {
         Console { }
     }
 
@@ -29,20 +29,13 @@ impl 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();
     });
 }
 

diff --git a/src/main.rs b/src/main.rs
line changes: +6/-3
index 876db9d..5d107a4
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,6 +4,7 @@
 
 mod allocator;
 mod asm;
+mod capabilities;
 mod console;
 mod panic;
 mod peripherals;
@@ -17,6 +18,7 @@ use cortex_m::asm::wfi;
 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};
 
@@ -39,6 +41,7 @@ fn main() -> ! {
     init_peripherals();
     init_console();
     init_tasks();
+    init_capabilities();
 
     let ticks = timer::ticks();
     kprintf!("Kernel started: {} ticks\r\n", ticks);
@@ -68,8 +71,8 @@ fn main() -> ! {
     */
 
     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 {
@@ -101,4 +104,4 @@ fn task_two() -> ! {
         on = !on;
         stdlib::sleep(223);
     }
-} 
\ No newline at end of file
+}

diff --git a/src/svc.rs b/src/svc.rs
line changes: +10/-1
index 291104d..39cc3ab
--- a/src/svc.rs
+++ b/src/svc.rs
@@ -2,9 +2,10 @@ use core::arch::global_asm;
 
 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!(
@@ -30,10 +31,18 @@ unsafe fn svcall_handler(regs: Option<&mut TaskRegisters>) {
             });
         }
         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 {

diff --git a/src/task.rs b/src/task.rs
line changes: +30/-16
index 7bf833b..70d7c8b
--- a/src/task.rs
+++ b/src/task.rs
@@ -9,14 +9,15 @@ pub use entry::TaskState;
 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],
@@ -27,7 +28,7 @@ pub struct TaskManager {
 }
 
 impl TaskManager {
-    fn new() -> TaskManager {
+    const fn new() -> TaskManager {
         TaskManager {
             region_map: [None; 10],
             next_tid: 0,
@@ -45,13 +46,21 @@ impl TaskManager {
         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;
@@ -74,8 +83,11 @@ impl TaskManager {
             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) {
@@ -132,13 +144,15 @@ impl TaskManager {
         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
@@ -147,14 +161,14 @@ where
 {
     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) -> !;
 }
@@ -186,7 +200,7 @@ _launch:
 
 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()
     });

diff --git a/src/task/entry.rs b/src/task/entry.rs
line changes: +4/-2
index 04cb4d4..687af1f
--- a/src/task/entry.rs
+++ b/src/task/entry.rs
@@ -1,4 +1,5 @@
-use crate::task::TaskID;
+use crate::capabilities::{CapToken, MAX_CAPS};
+use crate::task::TaskId;
 use crate::timer::Ticks;
 
 #[derive(Debug, PartialEq)]
@@ -56,10 +57,11 @@ impl TaskRegisters {
 
 #[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>,
 }

diff --git a/src/task/error.rs b/src/task/error.rs
line changes: +4/-0
index a351b20..92f21b5
--- a/src/task/error.rs
+++ b/src/task/error.rs
@@ -3,12 +3,16 @@ use core::fmt;
 #[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"),
         }
     }
 }