use super::{
address_space::{AddressSpace, TaskSlot},
alloc_kernel_object_id,
event::Event,
KernelObject,
KernelObjectId,
KernelObjectType,
};
use crate::{
memory::{KernelStackAllocator, PhysicalMemoryManager, Stack},
Platform,
};
use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
use core::{
cell::UnsafeCell,
sync::atomic::{AtomicU32, Ordering},
};
use hal::memory::VAddr;
use poplar::{caps::Capability, Handle};
use spinning_top::{RwSpinlock, Spinlock};
#[derive(Clone, Debug)]
pub enum TaskBlock {
OnEvent(Arc<Event>),
}
#[derive(Clone, Debug)]
pub enum TaskState {
Ready,
Running,
Blocked(TaskBlock),
}
impl TaskState {
pub fn is_ready(&self) -> bool {
match self {
TaskState::Ready => true,
_ => false,
}
}
pub fn is_running(&self) -> bool {
match self {
TaskState::Running => true,
_ => false,
}
}
pub fn is_blocked(&self) -> bool {
match self {
TaskState::Blocked(_) => true,
_ => false,
}
}
}
#[derive(Debug)]
pub enum TaskCreationError {
InvalidName,
NameTooLong,
InvalidCapabilityEncoding,
AddressSpaceFull,
NoKernelStackSlots,
}
pub struct Task<P>
where
P: Platform,
{
id: KernelObjectId,
owner: KernelObjectId,
pub name: String,
pub address_space: Arc<AddressSpace<P>>,
pub state: Spinlock<TaskState>,
pub capabilities: Vec<Capability>,
pub user_slot: Spinlock<TaskSlot>,
pub kernel_stack: Spinlock<Stack>,
pub kernel_stack_pointer: UnsafeCell<VAddr>,
pub user_stack_pointer: UnsafeCell<VAddr>,
pub context: UnsafeCell<P::TaskContext>,
pub handles: RwSpinlock<BTreeMap<Handle, Arc<dyn KernelObject>>>,
next_handle: AtomicU32,
}
unsafe impl<P> Send for Task<P> where P: Platform {}
unsafe impl<P> Sync for Task<P> where P: Platform {}
impl<P> Task<P>
where
P: Platform,
{
pub fn from_boot_info(
owner: KernelObjectId,
address_space: Arc<AddressSpace<P>>,
image: &seed::boot_info::LoadedImage,
allocator: &PhysicalMemoryManager,
kernel_page_table: &mut P::PageTable,
kernel_stack_allocator: &mut KernelStackAllocator<P>,
) -> Result<Arc<Task<P>>, TaskCreationError> {
let id = alloc_kernel_object_id();
let task_slot =
address_space.alloc_task_slot(0x4000, allocator).ok_or(TaskCreationError::AddressSpaceFull)?;
let kernel_stack = kernel_stack_allocator
.alloc_kernel_stack(0x4000, allocator, kernel_page_table)
.ok_or(TaskCreationError::NoKernelStackSlots)?;
let (kernel_stack_pointer, user_stack_pointer) =
unsafe { P::initialize_task_stacks(&kernel_stack, &task_slot.user_stack, image.entry_point) };
let context = P::new_task_context(kernel_stack_pointer, user_stack_pointer, image.entry_point);
Ok(Arc::new(Task {
id,
owner,
name: String::from(image.name.as_str()),
address_space,
state: Spinlock::new(TaskState::Ready),
capabilities: decode_capabilities(&image.capability_stream)?,
user_slot: Spinlock::new(task_slot),
kernel_stack: Spinlock::new(kernel_stack),
kernel_stack_pointer: UnsafeCell::new(kernel_stack_pointer),
user_stack_pointer: UnsafeCell::new(user_stack_pointer),
context: UnsafeCell::new(context),
handles: RwSpinlock::new(BTreeMap::new()),
next_handle: AtomicU32::new(1),
}))
}
pub fn add_handle(&self, object: Arc<dyn KernelObject>) -> Handle {
let handle_num = self.next_handle.fetch_add(1, Ordering::Relaxed);
self.handles.write().insert(Handle(handle_num), object);
Handle(handle_num)
}
}
impl<P> KernelObject for Task<P>
where
P: Platform,
{
fn id(&self) -> KernelObjectId {
self.id
}
fn typ(&self) -> KernelObjectType {
KernelObjectType::Task
}
}
fn decode_capabilities(mut cap_stream: &[u8]) -> Result<Vec<Capability>, TaskCreationError> {
use poplar::caps::*;
let mut caps = Vec::new();
macro_rules! one_byte_cap {
($cap: path) => {{
caps.push($cap);
cap_stream = &cap_stream[1..];
}};
}
while cap_stream.len() > 0 {
match cap_stream[0] {
CAP_GET_FRAMEBUFFER => one_byte_cap!(Capability::GetFramebuffer),
CAP_EARLY_LOGGING => one_byte_cap!(Capability::EarlyLogging),
CAP_SERVICE_PROVIDER => one_byte_cap!(Capability::ServiceProvider),
CAP_SERVICE_USER => one_byte_cap!(Capability::ServiceUser),
CAP_PCI_BUS_DRIVER => one_byte_cap!(Capability::PciBusDriver),
0x00 => cap_stream = &cap_stream[1..],
_ => return Err(TaskCreationError::InvalidCapabilityEncoding),
}
}
Ok(caps)
}