kernel/sched/
task.rs

1//! This module provides the basic task and thread structures for the scheduler.
2use core::num::NonZero;
3use core::ops::Range;
4use core::ptr::NonNull;
5
6use hal::Stack;
7
8use hal::stack::Stacklike;
9
10use crate::mem;
11
12use crate::mem::alloc::{Allocator, BestFitAllocator};
13use crate::sched::thread::{ThreadDescriptor, ThreadId, Timing};
14use crate::utils::KernelError;
15
16/// Id of a task. This is unique across all tasks.
17#[repr(u16)]
18#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
19pub enum TaskId {
20    // Task with normal user privileges in user mode.
21    User(usize),
22    // Task with kernel privileges in user mode.
23    Kernel(usize),
24}
25
26impl TaskId {
27    /// Check if the task is a user task.
28    pub fn is_user(&self) -> bool {
29        matches!(self, TaskId::User(_))
30    }
31
32    /// Check if the task is a kernel task.
33    pub fn is_kernel(&self) -> bool {
34        matches!(self, TaskId::Kernel(_))
35    }
36
37    pub fn new_user(id: usize) -> Self {
38        TaskId::User(id)
39    }
40
41    pub fn new_kernel(id: usize) -> Self {
42        TaskId::Kernel(id)
43    }
44}
45
46impl Into<usize> for TaskId {
47    fn into(self) -> usize {
48        match self {
49            TaskId::User(id) => id,
50            TaskId::Kernel(id) => id,
51        }
52    }
53}
54
55/// Descibes a task.
56pub struct TaskDescriptor {
57    /// The size of the memory that the task requires.
58    pub mem_size: usize,
59}
60
61/// The struct representing a task.
62pub struct Task {
63    /// The unique identifier of the task.
64    pub id: TaskId,
65    /// The memory of the task.
66    memory: TaskMemory,
67    /// The counter for the thread ids.
68    tid_cntr: usize,
69    /// The threads associated with the task.
70    threads: mem::array::Vec<ThreadId, 4>,
71}
72
73impl Task {
74    /// Create a new task.
75    ///
76    /// `memory_size` - The size of the memory that the task requires.
77    ///
78    /// Returns a new task if the task was created successfully, or an error if the task could not be created.
79    pub fn new(memory_size: usize, id: TaskId) -> Result<Self, KernelError> {
80        let memory = TaskMemory::new(memory_size)?;
81        let threads = mem::array::Vec::new();
82
83        Ok(Self {
84            id,
85            memory,
86            tid_cntr: 0,
87            threads,
88        })
89    }
90
91    fn allocate_tid(&mut self) -> ThreadId {
92        let tid = self.tid_cntr;
93        self.tid_cntr += 1;
94
95        ThreadId::new(tid, self.id)
96    }
97
98    pub fn create_thread(
99        &mut self,
100        entry: extern "C" fn(),
101        fin: Option<extern "C" fn() -> !>,
102        timing: Timing,
103    ) -> Result<ThreadDescriptor, KernelError> {
104        // Safe unwrap because stack size is non zero.
105        // TODO: Make this configurable
106        let stack_size = NonZero::new(4096usize).unwrap();
107        // TODO: Revert if error occurs
108        let stack_mem = self.memory.malloc(stack_size.into(), align_of::<u128>())?;
109        let stack_top = unsafe { stack_mem.byte_add(stack_size.get()) };
110
111        let stack = hal::stack::StackDescriptor {
112            top: stack_top,
113            size: stack_size,
114            entry,
115            fin,
116        };
117
118        let stack = unsafe { Stack::new(stack) }?;
119
120        let tid = self.allocate_tid();
121
122        // TODO: Revert if error occurs
123        self.register_thread(tid)?;
124
125        Ok(ThreadDescriptor { tid, stack, timing })
126    }
127
128    /// Register a thread with the task.
129    ///
130    /// `thread_id` - The id of the thread to register.
131    ///
132    /// Returns `Ok(())` if the thread was registered successfully, or an error if the thread could not be registered. TODO: Check if the thread is using the same memory as the task.
133    fn register_thread(&mut self, thread_id: ThreadId) -> Result<(), KernelError> {
134        self.threads.push(thread_id)
135    }
136}
137
138/// The memory of a task.
139pub struct TaskMemory {
140    /// The beginning of the memory.
141    begin: NonNull<u8>,
142    /// The size of the memory.
143    size: usize,
144
145    /// The allocator for the task's memory.
146    alloc: BestFitAllocator,
147}
148
149impl TaskMemory {
150    /// Create a new task memory.
151    ///
152    /// `size` - The size of the memory.
153    ///
154    /// Returns a new task memory if the memory was created successfully, or an error if the memory could not be created.
155    pub fn new(size: usize) -> Result<Self, KernelError> {
156        let begin = mem::malloc(size, align_of::<u128>()).ok_or(KernelError::OutOfMemory)?;
157
158        let mut alloc = BestFitAllocator::new();
159        let range = Range {
160            start: begin.as_ptr() as usize,
161            end: begin.as_ptr() as usize + size,
162        };
163
164        if let Err(e) = unsafe { alloc.add_range(range) } {
165            unsafe { mem::free(begin, size) };
166            return Err(e);
167        }
168
169        Ok(Self { begin, size, alloc })
170    }
171
172    pub fn malloc<T>(&mut self, size: usize, align: usize) -> Result<NonNull<T>, KernelError> {
173        self.alloc.malloc(size, align)
174    }
175
176    pub fn free<T>(&mut self, ptr: NonNull<T>, size: usize) {
177        unsafe { self.alloc.free(ptr, size) }
178    }
179}
180
181impl Drop for TaskMemory {
182    fn drop(&mut self) {
183        unsafe { mem::free(self.begin, self.size) };
184    }
185}