Files
k-os/crates/kernel/src/lib.rs

274 lines
8.6 KiB
Rust

#![no_std]
pub mod interrupt;
#[cfg(kani)]
pub mod proofs;
pub mod syscall;
pub use syscall::{SyscallOutcome, SyscallResult};
#[cfg(test)]
#[path = "tests/mod.rs"]
mod tests;
use domain::{
capability::{Capability, CapabilityTable},
endpoint::EndpointTable,
error::KernelError,
irq::IrqRoutingTable,
memory::{
page_pool::PagePool,
process_memory::ProcessMemory,
RegionTable,
},
message::Message,
process::{ProcessTable, MAX_PROCESSES},
resource::Resource,
scheduler::{multi_core::MultiCoreScheduler, Scheduler},
syscall::WaitSet,
thread::{ThreadTable, ThreadState, MAX_THREADS},
};
use platform::Platform;
use types::{
bounded::{CapSlot, Priority},
generation::Generation,
id::{ProcessId, ThreadId},
permission::{Permission, PermissionSet},
time::TimeSlice,
};
pub struct Kernel<P: Platform> {
platform: P,
processes: ProcessTable,
threads: ThreadTable,
endpoints: EndpointTable,
scheduler: MultiCoreScheduler,
page_pool: PagePool,
process_memory: [Option<ProcessMemory>; MAX_PROCESSES],
regions: RegionTable,
irqs: IrqRoutingTable,
pending_messages: [Option<Message>; MAX_THREADS],
pending_results: [Option<SyscallResult>; MAX_THREADS],
wait_sets: [Option<WaitSet>; MAX_THREADS],
}
impl<P: Platform> Kernel<P> {
pub fn new(platform: P, page_pool: PagePool) -> Self {
let num_cores = platform.num_cores();
Self {
platform,
processes: ProcessTable::new(),
threads: ThreadTable::new(),
endpoints: EndpointTable::new(),
scheduler: MultiCoreScheduler::new(num_cores),
page_pool,
process_memory: [const { None }; MAX_PROCESSES],
regions: RegionTable::new(),
irqs: IrqRoutingTable::new(),
pending_messages: [const { None }; MAX_THREADS],
pending_results: [const { None }; MAX_THREADS],
wait_sets: [const { None }; MAX_THREADS],
}
}
pub fn boot(
&mut self,
priority: Priority,
time_slice: TimeSlice,
) -> Result<(ProcessId, ThreadId), KernelError> {
let pid = self.processes.create(CapabilityTable::new(), None)?;
let tid = self.threads.spawn(pid, priority, time_slice, None)?;
self.processes.get_mut(pid)?.add_thread(tid)?;
self.process_memory[pid.raw() as usize] =
Some(ProcessMemory::new(pid, 1024));
let cap = Capability::new(
Resource::ProcessCreate,
PermissionSet::empty().with(Permission::Execute),
Generation::initial(),
);
self.processes.get_mut(pid)?.capabilities_mut().insert(cap)?;
let core = self.platform.core_id();
self.scheduler.add(tid, priority, core);
self.scheduler.next(core);
self.threads.get_mut(tid)?.set_running(core)?;
Ok((pid, tid))
}
pub fn platform_mut(&mut self) -> &mut P {
&mut self.platform
}
pub fn pending_result(&self, thread: ThreadId) -> Option<&SyscallResult> {
self.pending_results[thread.raw() as usize].as_ref()
}
pub fn free_page_count(&self) -> usize {
self.page_pool.free_count()
}
pub fn process_exists(&self, pid: ProcessId) -> bool {
self.processes.get(pid).is_ok()
}
pub fn thread_context(&self, tid: ThreadId) -> Option<&domain::thread::SavedContext> {
self.threads.get(tid).ok().map(|t| t.saved_context())
}
pub fn thread_context_mut(&mut self, tid: ThreadId) -> Option<&mut domain::thread::SavedContext> {
self.threads.get_mut(tid).ok().map(|t| t.saved_context_mut())
}
pub fn thread_process(&self, tid: ThreadId) -> Option<ProcessId> {
self.threads.get(tid).ok().map(|t| t.process())
}
pub fn current_thread(&self) -> Option<ThreadId> {
let core = self.platform.core_id();
self.scheduler.current(core)
}
pub fn pick_next_thread(&mut self) -> Option<ThreadId> {
use domain::scheduler::Scheduler;
let core = self.platform.core_id();
self.scheduler.next(core)
}
pub fn create_endpoint(&mut self) -> Result<types::id::EndpointId, KernelError> {
self.endpoints.create().ok_or(KernelError::NoFreeSlot)
}
pub fn grant_capability(
&mut self,
to: ProcessId,
resource: Resource,
permissions: PermissionSet,
) -> Result<CapSlot, KernelError> {
self.grant_cap(to, resource, permissions)
}
pub fn switch_to_thread(&mut self, tid: ThreadId) {
use domain::scheduler::Scheduler;
let core = self.platform.core_id();
if let Some(current) = self.scheduler.current(core) {
if current == tid {
return;
}
if let Ok(t) = self.threads.get_mut(current) {
if matches!(t.state(), ThreadState::Running(_)) {
let priority = t.priority();
let _ = t.preempt();
self.scheduler.add(current, priority, core);
}
}
}
if let Ok(t) = self.threads.get(tid) {
if matches!(t.state(), ThreadState::Ready) {
self.scheduler.next(core);
let _ = self.threads.get_mut(tid).map(|t| t.set_running(core));
}
}
}
fn caller(&self) -> Result<(ThreadId, ProcessId), KernelError> {
let core = self.platform.core_id();
let tid = self.scheduler.current(core)
.ok_or(KernelError::WouldBlock)?;
let thread = self.threads.get(tid)?;
match thread.state() {
ThreadState::Running(_) => Ok((tid, thread.process())),
_ => Err(KernelError::WouldBlock),
}
}
pub(crate) fn cleanup_threads(&mut self, thread_ids: &[Option<ThreadId>; 4]) {
use domain::scheduler::Scheduler;
for slot in thread_ids {
if let Some(tid) = slot {
self.endpoints.remove_thread_from_all(*tid);
self.cleanup_wait_any(*tid);
self.scheduler.remove(*tid);
self.pending_messages[tid.raw() as usize] = None;
self.pending_results[tid.raw() as usize] = None;
let _ = self.threads.destroy(*tid);
}
}
}
pub(crate) fn cleanup_wait_any(&mut self, tid: ThreadId) {
if let Some(wait_set) = self.wait_sets[tid.raw() as usize].take() {
for ep_slot in wait_set.endpoints() {
if let Some(_slot) = ep_slot {
self.endpoints.remove_thread_from_all(tid);
break;
}
}
}
}
fn check_cap(
&self,
owner: ProcessId,
slot: CapSlot,
needed: Permission,
) -> Result<&Capability, KernelError> {
let process = self.processes.get(owner)?;
let cap = process.capabilities().get(slot)?;
if !cap.permissions().allows(needed) {
return Err(KernelError::PermissionDenied { slot, needed });
}
let current_gen = self.resource_generation(cap.resource());
if !cap.generation().is_valid_against(&current_gen) {
return Err(KernelError::RevokedCapability(slot));
}
Ok(cap)
}
fn resource_generation(&self, resource: &Resource) -> Generation {
match resource {
Resource::Endpoint(id) => self
.endpoints
.get(*id)
.map(|ep| *ep.generation())
.unwrap_or_else(|| Generation::initial()),
Resource::Memory(id) => self
.regions
.get(*id)
.map(|r| *r.generation())
.unwrap_or_else(|| Generation::initial()),
_ => Generation::initial(),
}
}
pub fn revoke_resource(&mut self, resource: &Resource) {
match resource {
Resource::Endpoint(id) => {
if let Some(ep) = self.endpoints.get_mut(*id) {
ep.revoke();
}
}
Resource::Memory(id) => {
if let Some(r) = self.regions.get_mut(*id) {
r.revoke();
}
}
_ => {}
}
}
fn grant_cap(
&mut self,
to: ProcessId,
resource: Resource,
permissions: PermissionSet,
) -> Result<CapSlot, KernelError> {
let generation = self.resource_generation(&resource);
let cap = Capability::new(resource, permissions, generation);
self.processes.get_mut(to)?.capabilities_mut().insert(cap)
}
}