diff --git a/crates/kernel/src/syscall.rs b/crates/kernel/src/syscall.rs deleted file mode 100644 index c4410ca..0000000 --- a/crates/kernel/src/syscall.rs +++ /dev/null @@ -1,673 +0,0 @@ -use domain::syscall::Syscall; -use platform::Platform; - -use crate::{Kernel, SyscallOutcome, SyscallResult}; - -impl Kernel

{ - pub fn handle_syscall(&mut self, syscall: Syscall) -> SyscallOutcome { - let (tid, pid) = match self.caller() { - Ok(caller) => caller, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - match syscall { - Syscall::GetTime => self.handle_get_time(), - Syscall::GetProcessId => self.handle_get_process_id(pid), - Syscall::Yield => self.handle_yield(tid), - Syscall::Exit => self.handle_exit(tid, pid), - Syscall::ExitThread => self.handle_exit_thread(tid, pid), - Syscall::RequestPages { count } => self.handle_request_pages(pid, count), - Syscall::ReleasePages { addr, count } => self.handle_release_pages(pid, addr, count), - Syscall::SpawnThread { entry, stack } => self.handle_spawn_thread(pid, entry, stack), - Syscall::CreateProcess { create_cap, image } => self.handle_create_process(pid, create_cap, image), - Syscall::Kill { process } => self.handle_kill(pid, process), - Syscall::Delegate { slot, to_endpoint, permissions } => { - self.handle_delegate(pid, slot, to_endpoint, permissions) - } - Syscall::Send { endpoint, message } => self.handle_send(tid, pid, endpoint, message), - Syscall::Receive { endpoint } => self.handle_receive(tid, pid, endpoint), - Syscall::WaitForInterrupt { irq } => self.handle_wait_for_interrupt(tid, pid, irq), - Syscall::AcknowledgeInterrupt { irq } => self.handle_ack_interrupt(pid, irq), - Syscall::WaitAny { sources } => self.handle_wait_any(tid, pid, sources), - } - } - - fn handle_get_time(&self) -> SyscallOutcome { - let ticks = self.platform.current_ticks(); - SyscallOutcome::Complete(SyscallResult::Value(ticks.raw() as usize)) - } - - fn handle_get_process_id(&self, pid: types::id::ProcessId) -> SyscallOutcome { - SyscallOutcome::Complete(SyscallResult::Value(pid.raw() as usize)) - } - - fn handle_exit(&mut self, caller_tid: types::id::ThreadId, pid: types::id::ProcessId) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - // Kill all threads of this process - let thread_ids: [Option; 4] = { - match self.processes.get(pid) { - Ok(p) => { - let mut ids = [None; 4]; - for (i, tid) in p.thread_ids().enumerate() { - if i < 4 { - ids[i] = Some(tid); - } - } - ids - } - Err(_) => return SyscallOutcome::TerminateCaller, - } - }; - - self.cleanup_threads(&thread_ids); - - let _ = self.processes.destroy(pid); - self.process_memory[pid.raw() as usize] = None; - SyscallOutcome::TerminateCaller - } - - fn handle_exit_thread(&mut self, caller_tid: types::id::ThreadId, pid: types::id::ProcessId) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - self.scheduler.remove(caller_tid); - let _ = self.threads.destroy(caller_tid); - - if let Ok(process) = self.processes.get_mut(pid) { - process.remove_thread(caller_tid); - if process.thread_ids().count() == 0 { - let _ = self.processes.destroy(pid); - self.process_memory[pid.raw() as usize] = None; - } - } - - SyscallOutcome::TerminateCaller - } - - fn handle_yield(&mut self, caller_tid: types::id::ThreadId) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - let core = self.platform.core_id(); - let thread = match self.threads.get_mut(caller_tid) { - Ok(t) => t, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - let priority = thread.priority(); - if thread.preempt().is_err() { - return SyscallOutcome::Complete(SyscallResult::Ok); - } - - self.scheduler.add(caller_tid, priority, core); - - match self.scheduler.next(core) { - Some(next) if next != caller_tid => { - if self.threads.get_mut(next).and_then(|t| t.set_running(core)).is_ok() { - SyscallOutcome::SwitchTo(next) - } else { - let _ = self.threads.get_mut(caller_tid).map(|t| t.set_running(core)); - SyscallOutcome::Complete(SyscallResult::Ok) - } - } - _ => { - let _ = self.threads.get_mut(caller_tid).map(|t| t.set_running(core)); - SyscallOutcome::Complete(SyscallResult::Ok) - } - } - } - - fn handle_request_pages( - &mut self, - pid: types::id::ProcessId, - count: types::bounded::PageCount, - ) -> SyscallOutcome { - let pages = count.value() as usize; - - if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { - if pm.grant(pages).is_err() { - return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::OutOfMemory, - )); - } - } else { - return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::InvalidProcess(pid), - )); - } - - let addr = match self.page_pool.allocate(pages) { - Some(a) => a, - None => { - if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { - let _ = pm.release(pages); - } - return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::OutOfMemory, - )); - } - }; - - let region = domain::memory::MemoryRegion::new( - types::id::RegionId::from_raw(0), - pid, - addr, - count, - types::generation::Generation::initial(), - ); - let region_id = match self.regions.insert(region) { - Some(id) => id, - None => { - self.page_pool.free(addr, pages); - if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { - let _ = pm.release(pages); - } - return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::OutOfMemory, - )); - } - }; - - let slot = match self.grant_cap( - pid, - domain::resource::Resource::Memory(region_id), - types::permission::PermissionSet::empty() - .with(types::permission::Permission::Read) - .with(types::permission::Permission::Write), - ) { - Ok(s) => s, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - SyscallOutcome::Complete(SyscallResult::Slot(slot)) - } - - fn handle_release_pages( - &mut self, - pid: types::id::ProcessId, - addr: types::address::VirtualAddress, - count: types::bounded::PageCount, - ) -> SyscallOutcome { - let pages = count.value() as usize; - let phys = types::address::PhysicalAddress::from_raw(addr.raw()); - - let region_id = match self.regions.find_by_base(pid, phys) { - Some((rid, _)) => rid, - None => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::OutOfMemory, - )), - }; - - if !self.page_pool.free(phys, pages) { - return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::OutOfMemory, - )); - } - - let _ = self.regions.remove(region_id); - - if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { - let _ = pm.release(pages); - } - - SyscallOutcome::Complete(SyscallResult::Ok) - } - - fn handle_spawn_thread( - &mut self, - pid: types::id::ProcessId, - _entry: types::address::EntryPoint, - _stack: types::address::StackPointer, - ) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - let priority = types::bounded::Priority::new(16) - .unwrap_or_else(|| unreachable!()); - let time_slice = types::time::TimeSlice::new(100); - - let tid = match self.threads.spawn(pid, priority, time_slice, None) { - Ok(t) => t, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - if let Err(e) = self.processes.get_mut(pid).and_then(|p| p.add_thread(tid)) { - let _ = self.threads.destroy(tid); - return SyscallOutcome::Complete(SyscallResult::Error(e)); - } - - let core = self.platform.core_id(); - self.scheduler.add(tid, priority, core); - SyscallOutcome::Complete(SyscallResult::Value(tid.raw() as usize)) - } - - fn handle_create_process( - &mut self, - caller_pid: types::id::ProcessId, - create_cap: types::bounded::CapSlot, - _image: types::address::DataRef, - ) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - if let Err(e) = self.check_cap(caller_pid, create_cap, types::permission::Permission::Execute) { - return SyscallOutcome::Complete(SyscallResult::Error(e)); - } - - let new_pid = match self.processes.create( - domain::capability::CapabilityTable::new(), - None, - ) { - Ok(p) => p, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - self.process_memory[new_pid.raw() as usize] = - Some(domain::memory::process_memory::ProcessMemory::new(new_pid, 1024)); - - let priority = types::bounded::Priority::new(16) - .unwrap_or_else(|| unreachable!()); - let time_slice = types::time::TimeSlice::new(100); - - let tid = match self.threads.spawn(new_pid, priority, time_slice, None) { - Ok(t) => t, - Err(e) => { - let _ = self.processes.destroy(new_pid); - return SyscallOutcome::Complete(SyscallResult::Error(e)); - } - }; - - if let Err(e) = self.processes.get_mut(new_pid).and_then(|p| p.add_thread(tid)) { - let _ = self.threads.destroy(tid); - let _ = self.processes.destroy(new_pid); - return SyscallOutcome::Complete(SyscallResult::Error(e)); - } - - let core = self.platform.core_id(); - self.scheduler.add(tid, priority, core); - - let slot = match self.grant_cap( - caller_pid, - domain::resource::Resource::Process(new_pid), - types::permission::PermissionSet::empty() - .with(types::permission::Permission::Execute) - .with(types::permission::Permission::Read), - ) { - Ok(s) => s, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - SyscallOutcome::Complete(SyscallResult::Slot(slot)) - } - - fn handle_kill( - &mut self, - caller_pid: types::id::ProcessId, - process_cap: types::bounded::CapSlot, - ) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - let target_pid = match self.check_cap(caller_pid, process_cap, types::permission::Permission::Execute) { - Ok(cap) => match cap.resource() { - domain::resource::Resource::Process(pid) => *pid, - _ => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::PermissionDenied { - slot: process_cap, - needed: types::permission::Permission::Execute, - }, - )), - }, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - let thread_ids: [Option; 4] = { - match self.processes.get(target_pid) { - Ok(p) => { - let mut ids = [None; 4]; - for (i, tid) in p.thread_ids().enumerate() { - if i < 4 { ids[i] = Some(tid); } - } - ids - } - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - } - }; - - self.cleanup_threads(&thread_ids); - - let _ = self.processes.destroy(target_pid); - self.process_memory[target_pid.raw() as usize] = None; - SyscallOutcome::Complete(SyscallResult::Ok) - } - - fn handle_delegate( - &mut self, - caller_pid: types::id::ProcessId, - slot: types::bounded::CapSlot, - to_endpoint: types::bounded::CapSlot, - restrict: types::permission::PermissionSet, - ) -> SyscallOutcome { - let (resource, permissions) = match self.check_cap( - caller_pid, - slot, - types::permission::Permission::Delegate, - ) { - Ok(cap) => (cap.resource().clone(), *cap.permissions()), - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - let restricted = permissions.restrict_to(&restrict); - - let target_pid = match self.check_cap( - caller_pid, - to_endpoint, - types::permission::Permission::Execute, - ) { - Ok(cap) => match cap.resource() { - domain::resource::Resource::Process(pid) => *pid, - _ => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::PermissionDenied { - slot: to_endpoint, - needed: types::permission::Permission::Execute, - }, - )), - }, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - match self.grant_cap(target_pid, resource, restricted) { - Ok(_) => SyscallOutcome::Complete(SyscallResult::Ok), - Err(e) => SyscallOutcome::Complete(SyscallResult::Error(e)), - } - } - - fn handle_send( - &mut self, - caller_tid: types::id::ThreadId, - caller_pid: types::id::ProcessId, - endpoint_slot: types::bounded::CapSlot, - message: domain::message::Message, - ) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - let ep_id = match self.check_cap( - caller_pid, - endpoint_slot, - types::permission::Permission::Execute, - ) { - Ok(cap) => match cap.resource() { - domain::resource::Resource::Endpoint(id) => *id, - _ => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::InvalidEndpoint(types::id::EndpointId::from_raw(0)), - )), - }, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - let endpoint = match self.endpoints.get_mut(ep_id) { - Some(ep) => ep, - None => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::InvalidEndpoint(ep_id), - )), - }; - - if endpoint.has_waiting_receiver() { - let receiver_tid = match endpoint.dequeue_receiver() { - Some(t) => t, - None => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::WouldBlock, - )), - }; - - self.pending_results[receiver_tid.raw() as usize] = - Some(SyscallResult::Message(message)); - - let core = self.platform.core_id(); - - let receiver = match self.threads.get_mut(receiver_tid) { - Ok(t) => t, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - let _ = receiver.unblock(); - let _ = receiver.set_running(core); - - let caller = match self.threads.get_mut(caller_tid) { - Ok(t) => t, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - let caller_priority = caller.priority(); - let _ = caller.preempt(); - self.scheduler.add(caller_tid, caller_priority, core); - - self.scheduler.set_current(core, receiver_tid); - - SyscallOutcome::SwitchTo(receiver_tid) - } else { - self.pending_messages[caller_tid.raw() as usize] = Some(message); - - let endpoint = match self.endpoints.get_mut(ep_id) { - Some(ep) => ep, - None => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::InvalidEndpoint(ep_id), - )), - }; - let _ = endpoint.enqueue_sender(caller_tid); - - let caller = match self.threads.get_mut(caller_tid) { - Ok(t) => t, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - let _ = caller.block(domain::thread::BlockReason::Sending(endpoint_slot)); - self.scheduler.remove(caller_tid); - - SyscallOutcome::BlockCaller(domain::thread::BlockReason::Sending(endpoint_slot)) - } - } - - fn handle_receive( - &mut self, - caller_tid: types::id::ThreadId, - caller_pid: types::id::ProcessId, - endpoint_slot: types::bounded::CapSlot, - ) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - let ep_id = match self.check_cap( - caller_pid, - endpoint_slot, - types::permission::Permission::Read, - ) { - Ok(cap) => match cap.resource() { - domain::resource::Resource::Endpoint(id) => *id, - _ => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::InvalidEndpoint(types::id::EndpointId::from_raw(0)), - )), - }, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - let endpoint = match self.endpoints.get_mut(ep_id) { - Some(ep) => ep, - None => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::InvalidEndpoint(ep_id), - )), - }; - - if endpoint.has_waiting_sender() { - let sender_tid = match endpoint.dequeue_sender() { - Some(t) => t, - None => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::WouldBlock, - )), - }; - - let message = self.pending_messages[sender_tid.raw() as usize] - .take() - .unwrap_or_else(|| domain::message::Message::empty()); - - self.pending_results[sender_tid.raw() as usize] = Some(SyscallResult::Ok); - - let sender = match self.threads.get_mut(sender_tid) { - Ok(t) => t, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - let priority = sender.priority(); - let _ = sender.unblock(); - let core = self.platform.core_id(); - self.scheduler.add(sender_tid, priority, core); - - SyscallOutcome::Complete(SyscallResult::Message(message)) - } else { - let endpoint = match self.endpoints.get_mut(ep_id) { - Some(ep) => ep, - None => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::InvalidEndpoint(ep_id), - )), - }; - let _ = endpoint.enqueue_receiver(caller_tid); - - let caller = match self.threads.get_mut(caller_tid) { - Ok(t) => t, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - let _ = caller.block(domain::thread::BlockReason::Receiving(endpoint_slot)); - self.scheduler.remove(caller_tid); - - SyscallOutcome::BlockCaller(domain::thread::BlockReason::Receiving(endpoint_slot)) - } - } - - fn handle_wait_for_interrupt( - &mut self, - caller_tid: types::id::ThreadId, - caller_pid: types::id::ProcessId, - irq_slot: types::bounded::CapSlot, - ) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - let irq_id = match self.check_cap( - caller_pid, - irq_slot, - types::permission::Permission::Read, - ) { - Ok(cap) => match cap.resource() { - domain::resource::Resource::Interrupt(id) => *id, - _ => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)), - )), - }, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - let _ = self.irqs.register(irq_id, caller_tid); - - let caller = match self.threads.get_mut(caller_tid) { - Ok(t) => t, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - let _ = caller.block(domain::thread::BlockReason::WaitingForInterrupt(irq_slot)); - self.scheduler.remove(caller_tid); - - SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingForInterrupt(irq_slot)) - } - - fn handle_ack_interrupt( - &mut self, - caller_pid: types::id::ProcessId, - irq_slot: types::bounded::CapSlot, - ) -> SyscallOutcome { - let irq_id = match self.check_cap( - caller_pid, - irq_slot, - types::permission::Permission::Write, - ) { - Ok(cap) => match cap.resource() { - domain::resource::Resource::Interrupt(id) => *id, - _ => return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)), - )), - }, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - - self.platform.unmask_irq(irq_id); - SyscallOutcome::Complete(SyscallResult::Ok) - } - - fn handle_wait_any( - &mut self, - caller_tid: types::id::ThreadId, - caller_pid: types::id::ProcessId, - sources: domain::syscall::WaitSet, - ) -> SyscallOutcome { - use domain::scheduler::Scheduler; - - if sources.is_empty() { - return SyscallOutcome::Complete(SyscallResult::Error( - domain::error::KernelError::WouldBlock, - )); - } - - for ep_slot in sources.endpoints() { - if let Some(slot) = ep_slot { - let ep_id = match self.check_cap( - caller_pid, - *slot, - types::permission::Permission::Read, - ) { - Ok(cap) => match cap.resource() { - domain::resource::Resource::Endpoint(id) => *id, - _ => continue, - }, - Err(_) => continue, - }; - - if let Some(ep) = self.endpoints.get_mut(ep_id) { - if ep.has_waiting_sender() { - return SyscallOutcome::Complete(SyscallResult::WakeReason( - domain::syscall::WakeReason::MessageReady(*slot), - )); - } - } - } - } - - for irq_slot in sources.interrupts() { - if let Some(slot) = irq_slot { - if let Ok(cap) = self.check_cap( - caller_pid, - *slot, - types::permission::Permission::Read, - ) { - if let domain::resource::Resource::Interrupt(irq_id) = cap.resource() { - if self.irqs.owner(*irq_id).is_some() { - return SyscallOutcome::Complete(SyscallResult::WakeReason( - domain::syscall::WakeReason::InterruptFired(*slot), - )); - } - } - } - } - } - - for ep_slot in sources.endpoints() { - if let Some(slot) = ep_slot { - if let Ok(cap) = self.check_cap(caller_pid, *slot, types::permission::Permission::Read) { - if let domain::resource::Resource::Endpoint(id) = cap.resource() { - if let Some(ep) = self.endpoints.get_mut(*id) { - let _ = ep.enqueue_receiver(caller_tid); - } - } - } - } - } - - self.wait_sets[caller_tid.raw() as usize] = Some(sources); - - let caller = match self.threads.get_mut(caller_tid) { - Ok(t) => t, - Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), - }; - let _ = caller.block(domain::thread::BlockReason::WaitingAny); - self.scheduler.remove(caller_tid); - - SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingAny) - } -} diff --git a/crates/kernel/src/syscall/capability.rs b/crates/kernel/src/syscall/capability.rs new file mode 100644 index 0000000..3a0966b --- /dev/null +++ b/crates/kernel/src/syscall/capability.rs @@ -0,0 +1,46 @@ +use platform::Platform; + +use crate::{Kernel, SyscallOutcome, SyscallResult}; + +impl Kernel

{ + pub(crate) fn handle_delegate( + &mut self, + caller_pid: types::id::ProcessId, + slot: types::bounded::CapSlot, + to_endpoint: types::bounded::CapSlot, + restrict: types::permission::PermissionSet, + ) -> SyscallOutcome { + let (resource, permissions) = match self.check_cap( + caller_pid, + slot, + types::permission::Permission::Delegate, + ) { + Ok(cap) => (cap.resource().clone(), *cap.permissions()), + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + let restricted = permissions.restrict_to(&restrict); + + let target_pid = match self.check_cap( + caller_pid, + to_endpoint, + types::permission::Permission::Execute, + ) { + Ok(cap) => match cap.resource() { + domain::resource::Resource::Process(pid) => *pid, + _ => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::PermissionDenied { + slot: to_endpoint, + needed: types::permission::Permission::Execute, + }, + )), + }, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + match self.grant_cap(target_pid, resource, restricted) { + Ok(_) => SyscallOutcome::Complete(SyscallResult::Ok), + Err(e) => SyscallOutcome::Complete(SyscallResult::Error(e)), + } + } +} diff --git a/crates/kernel/src/syscall/ipc.rs b/crates/kernel/src/syscall/ipc.rs new file mode 100644 index 0000000..2270708 --- /dev/null +++ b/crates/kernel/src/syscall/ipc.rs @@ -0,0 +1,298 @@ +use platform::Platform; + +use crate::{Kernel, SyscallOutcome, SyscallResult}; + +impl Kernel

{ + pub(crate) fn handle_send( + &mut self, + caller_tid: types::id::ThreadId, + caller_pid: types::id::ProcessId, + endpoint_slot: types::bounded::CapSlot, + message: domain::message::Message, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + let ep_id = match self.check_cap( + caller_pid, + endpoint_slot, + types::permission::Permission::Execute, + ) { + Ok(cap) => match cap.resource() { + domain::resource::Resource::Endpoint(id) => *id, + _ => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidEndpoint(types::id::EndpointId::from_raw(0)), + )), + }, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + let endpoint = match self.endpoints.get_mut(ep_id) { + Some(ep) => ep, + None => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidEndpoint(ep_id), + )), + }; + + if endpoint.has_waiting_receiver() { + let receiver_tid = match endpoint.dequeue_receiver() { + Some(t) => t, + None => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::WouldBlock, + )), + }; + + self.pending_results[receiver_tid.raw() as usize] = + Some(SyscallResult::Message(message)); + + let core = self.platform.core_id(); + + let receiver = match self.threads.get_mut(receiver_tid) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + let _ = receiver.unblock(); + let _ = receiver.set_running(core); + + let caller = match self.threads.get_mut(caller_tid) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + let caller_priority = caller.priority(); + let _ = caller.preempt(); + self.scheduler.add(caller_tid, caller_priority, core); + + self.scheduler.set_current(core, receiver_tid); + + SyscallOutcome::SwitchTo(receiver_tid) + } else { + self.pending_messages[caller_tid.raw() as usize] = Some(message); + + let endpoint = match self.endpoints.get_mut(ep_id) { + Some(ep) => ep, + None => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidEndpoint(ep_id), + )), + }; + let _ = endpoint.enqueue_sender(caller_tid); + + let caller = match self.threads.get_mut(caller_tid) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + let _ = caller.block(domain::thread::BlockReason::Sending(endpoint_slot)); + self.scheduler.remove(caller_tid); + + SyscallOutcome::BlockCaller(domain::thread::BlockReason::Sending(endpoint_slot)) + } + } + + pub(crate) fn handle_receive( + &mut self, + caller_tid: types::id::ThreadId, + caller_pid: types::id::ProcessId, + endpoint_slot: types::bounded::CapSlot, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + let ep_id = match self.check_cap( + caller_pid, + endpoint_slot, + types::permission::Permission::Read, + ) { + Ok(cap) => match cap.resource() { + domain::resource::Resource::Endpoint(id) => *id, + _ => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidEndpoint(types::id::EndpointId::from_raw(0)), + )), + }, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + let endpoint = match self.endpoints.get_mut(ep_id) { + Some(ep) => ep, + None => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidEndpoint(ep_id), + )), + }; + + if endpoint.has_waiting_sender() { + let sender_tid = match endpoint.dequeue_sender() { + Some(t) => t, + None => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::WouldBlock, + )), + }; + + let message = self.pending_messages[sender_tid.raw() as usize] + .take() + .unwrap_or_else(|| domain::message::Message::empty()); + + self.pending_results[sender_tid.raw() as usize] = Some(SyscallResult::Ok); + + let sender = match self.threads.get_mut(sender_tid) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + let priority = sender.priority(); + let _ = sender.unblock(); + let core = self.platform.core_id(); + self.scheduler.add(sender_tid, priority, core); + + SyscallOutcome::Complete(SyscallResult::Message(message)) + } else { + let endpoint = match self.endpoints.get_mut(ep_id) { + Some(ep) => ep, + None => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidEndpoint(ep_id), + )), + }; + let _ = endpoint.enqueue_receiver(caller_tid); + + let caller = match self.threads.get_mut(caller_tid) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + let _ = caller.block(domain::thread::BlockReason::Receiving(endpoint_slot)); + self.scheduler.remove(caller_tid); + + SyscallOutcome::BlockCaller(domain::thread::BlockReason::Receiving(endpoint_slot)) + } + } + + pub(crate) fn handle_wait_for_interrupt( + &mut self, + caller_tid: types::id::ThreadId, + caller_pid: types::id::ProcessId, + irq_slot: types::bounded::CapSlot, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + let irq_id = match self.check_cap( + caller_pid, + irq_slot, + types::permission::Permission::Read, + ) { + Ok(cap) => match cap.resource() { + domain::resource::Resource::Interrupt(id) => *id, + _ => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)), + )), + }, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + let _ = self.irqs.register(irq_id, caller_tid); + + let caller = match self.threads.get_mut(caller_tid) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + let _ = caller.block(domain::thread::BlockReason::WaitingForInterrupt(irq_slot)); + self.scheduler.remove(caller_tid); + + SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingForInterrupt(irq_slot)) + } + + pub(crate) fn handle_ack_interrupt( + &mut self, + caller_pid: types::id::ProcessId, + irq_slot: types::bounded::CapSlot, + ) -> SyscallOutcome { + let irq_id = match self.check_cap( + caller_pid, + irq_slot, + types::permission::Permission::Write, + ) { + Ok(cap) => match cap.resource() { + domain::resource::Resource::Interrupt(id) => *id, + _ => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)), + )), + }, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + self.platform.unmask_irq(irq_id); + SyscallOutcome::Complete(SyscallResult::Ok) + } + + pub(crate) fn handle_wait_any( + &mut self, + caller_tid: types::id::ThreadId, + caller_pid: types::id::ProcessId, + sources: domain::syscall::WaitSet, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + if sources.is_empty() { + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::WouldBlock, + )); + } + + for ep_slot in sources.endpoints() { + if let Some(slot) = ep_slot { + let ep_id = match self.check_cap( + caller_pid, + *slot, + types::permission::Permission::Read, + ) { + Ok(cap) => match cap.resource() { + domain::resource::Resource::Endpoint(id) => *id, + _ => continue, + }, + Err(_) => continue, + }; + + if let Some(ep) = self.endpoints.get_mut(ep_id) { + if ep.has_waiting_sender() { + return SyscallOutcome::Complete(SyscallResult::WakeReason( + domain::syscall::WakeReason::MessageReady(*slot), + )); + } + } + } + } + + for irq_slot in sources.interrupts() { + if let Some(slot) = irq_slot { + if let Ok(cap) = self.check_cap( + caller_pid, + *slot, + types::permission::Permission::Read, + ) { + if let domain::resource::Resource::Interrupt(irq_id) = cap.resource() { + if self.irqs.owner(*irq_id).is_some() { + return SyscallOutcome::Complete(SyscallResult::WakeReason( + domain::syscall::WakeReason::InterruptFired(*slot), + )); + } + } + } + } + } + + for ep_slot in sources.endpoints() { + if let Some(slot) = ep_slot { + if let Ok(cap) = self.check_cap(caller_pid, *slot, types::permission::Permission::Read) { + if let domain::resource::Resource::Endpoint(id) = cap.resource() { + if let Some(ep) = self.endpoints.get_mut(*id) { + let _ = ep.enqueue_receiver(caller_tid); + } + } + } + } + } + + self.wait_sets[caller_tid.raw() as usize] = Some(sources); + + let caller = match self.threads.get_mut(caller_tid) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + let _ = caller.block(domain::thread::BlockReason::WaitingAny); + self.scheduler.remove(caller_tid); + + SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingAny) + } +} diff --git a/crates/kernel/src/syscall/lifecycle.rs b/crates/kernel/src/syscall/lifecycle.rs new file mode 100644 index 0000000..fa6f905 --- /dev/null +++ b/crates/kernel/src/syscall/lifecycle.rs @@ -0,0 +1,128 @@ +use platform::Platform; + +use crate::{Kernel, SyscallOutcome, SyscallResult}; + +impl Kernel

{ + pub(crate) fn handle_spawn_thread( + &mut self, + pid: types::id::ProcessId, + _entry: types::address::EntryPoint, + _stack: types::address::StackPointer, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + let priority = types::bounded::Priority::new(16) + .unwrap_or_else(|| unreachable!()); + let time_slice = types::time::TimeSlice::new(100); + + let tid = match self.threads.spawn(pid, priority, time_slice, None) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + if let Err(e) = self.processes.get_mut(pid).and_then(|p| p.add_thread(tid)) { + let _ = self.threads.destroy(tid); + return SyscallOutcome::Complete(SyscallResult::Error(e)); + } + + let core = self.platform.core_id(); + self.scheduler.add(tid, priority, core); + SyscallOutcome::Complete(SyscallResult::Value(tid.raw() as usize)) + } + + pub(crate) fn handle_create_process( + &mut self, + caller_pid: types::id::ProcessId, + create_cap: types::bounded::CapSlot, + _image: types::address::DataRef, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + if let Err(e) = self.check_cap(caller_pid, create_cap, types::permission::Permission::Execute) { + return SyscallOutcome::Complete(SyscallResult::Error(e)); + } + + let new_pid = match self.processes.create( + domain::capability::CapabilityTable::new(), + None, + ) { + Ok(p) => p, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + self.process_memory[new_pid.raw() as usize] = + Some(domain::memory::process_memory::ProcessMemory::new(new_pid, 1024)); + + let priority = types::bounded::Priority::new(16) + .unwrap_or_else(|| unreachable!()); + let time_slice = types::time::TimeSlice::new(100); + + let tid = match self.threads.spawn(new_pid, priority, time_slice, None) { + Ok(t) => t, + Err(e) => { + let _ = self.processes.destroy(new_pid); + return SyscallOutcome::Complete(SyscallResult::Error(e)); + } + }; + + if let Err(e) = self.processes.get_mut(new_pid).and_then(|p| p.add_thread(tid)) { + let _ = self.threads.destroy(tid); + let _ = self.processes.destroy(new_pid); + return SyscallOutcome::Complete(SyscallResult::Error(e)); + } + + let core = self.platform.core_id(); + self.scheduler.add(tid, priority, core); + + let slot = match self.grant_cap( + caller_pid, + domain::resource::Resource::Process(new_pid), + types::permission::PermissionSet::empty() + .with(types::permission::Permission::Execute) + .with(types::permission::Permission::Read), + ) { + Ok(s) => s, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + SyscallOutcome::Complete(SyscallResult::Slot(slot)) + } + + pub(crate) fn handle_kill( + &mut self, + caller_pid: types::id::ProcessId, + process_cap: types::bounded::CapSlot, + ) -> SyscallOutcome { + let target_pid = match self.check_cap(caller_pid, process_cap, types::permission::Permission::Execute) { + Ok(cap) => match cap.resource() { + domain::resource::Resource::Process(pid) => *pid, + _ => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::PermissionDenied { + slot: process_cap, + needed: types::permission::Permission::Execute, + }, + )), + }, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + let thread_ids: [Option; 4] = { + match self.processes.get(target_pid) { + Ok(p) => { + let mut ids = [None; 4]; + for (i, tid) in p.thread_ids().enumerate() { + if i < 4 { ids[i] = Some(tid); } + } + ids + } + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + } + }; + + self.cleanup_threads(&thread_ids); + + let _ = self.processes.destroy(target_pid); + self.process_memory[target_pid.raw() as usize] = None; + SyscallOutcome::Complete(SyscallResult::Ok) + } +} diff --git a/crates/kernel/src/syscall/memory.rs b/crates/kernel/src/syscall/memory.rs new file mode 100644 index 0000000..573c715 --- /dev/null +++ b/crates/kernel/src/syscall/memory.rs @@ -0,0 +1,101 @@ +use platform::Platform; + +use crate::{Kernel, SyscallOutcome, SyscallResult}; + +impl Kernel

{ + pub(crate) fn handle_request_pages( + &mut self, + pid: types::id::ProcessId, + count: types::bounded::PageCount, + ) -> SyscallOutcome { + let pages = count.value() as usize; + + if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { + if pm.grant(pages).is_err() { + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::OutOfMemory, + )); + } + } else { + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidProcess(pid), + )); + } + + let addr = match self.page_pool.allocate(pages) { + Some(a) => a, + None => { + if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { + let _ = pm.release(pages); + } + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::OutOfMemory, + )); + } + }; + + let region = domain::memory::MemoryRegion::new( + types::id::RegionId::from_raw(0), + pid, + addr, + count, + types::generation::Generation::initial(), + ); + let region_id = match self.regions.insert(region) { + Some(id) => id, + None => { + self.page_pool.free(addr, pages); + if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { + let _ = pm.release(pages); + } + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::OutOfMemory, + )); + } + }; + + let slot = match self.grant_cap( + pid, + domain::resource::Resource::Memory(region_id), + types::permission::PermissionSet::empty() + .with(types::permission::Permission::Read) + .with(types::permission::Permission::Write), + ) { + Ok(s) => s, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + SyscallOutcome::Complete(SyscallResult::Slot(slot)) + } + + pub(crate) fn handle_release_pages( + &mut self, + pid: types::id::ProcessId, + addr: types::address::VirtualAddress, + count: types::bounded::PageCount, + ) -> SyscallOutcome { + let pages = count.value() as usize; + let phys = types::address::PhysicalAddress::from_raw(addr.raw()); + + let region_id = match self.regions.find_by_base(pid, phys) { + Some((rid, _)) => rid, + None => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::OutOfMemory, + )), + }; + + if !self.page_pool.free(phys, pages) { + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::OutOfMemory, + )); + } + + let _ = self.regions.remove(region_id); + + if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { + let _ = pm.release(pages); + } + + SyscallOutcome::Complete(SyscallResult::Ok) + } +} diff --git a/crates/kernel/src/syscall/mod.rs b/crates/kernel/src/syscall/mod.rs new file mode 100644 index 0000000..bb53d5e --- /dev/null +++ b/crates/kernel/src/syscall/mod.rs @@ -0,0 +1,40 @@ +mod capability; +mod ipc; +mod lifecycle; +mod memory; +mod simple; + +use domain::syscall::Syscall; +use platform::Platform; + +use crate::{Kernel, SyscallOutcome, SyscallResult}; + +impl Kernel

{ + pub fn handle_syscall(&mut self, syscall: Syscall) -> SyscallOutcome { + let (tid, pid) = match self.caller() { + Ok(caller) => caller, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + match syscall { + Syscall::GetTime => self.handle_get_time(), + Syscall::GetProcessId => self.handle_get_process_id(pid), + Syscall::Yield => self.handle_yield(tid), + Syscall::Exit => self.handle_exit(tid, pid), + Syscall::ExitThread => self.handle_exit_thread(tid, pid), + Syscall::RequestPages { count } => self.handle_request_pages(pid, count), + Syscall::ReleasePages { addr, count } => self.handle_release_pages(pid, addr, count), + Syscall::SpawnThread { entry, stack } => self.handle_spawn_thread(pid, entry, stack), + Syscall::CreateProcess { create_cap, image } => self.handle_create_process(pid, create_cap, image), + Syscall::Kill { process } => self.handle_kill(pid, process), + Syscall::Delegate { slot, to_endpoint, permissions } => { + self.handle_delegate(pid, slot, to_endpoint, permissions) + } + Syscall::Send { endpoint, message } => self.handle_send(tid, pid, endpoint, message), + Syscall::Receive { endpoint } => self.handle_receive(tid, pid, endpoint), + Syscall::WaitForInterrupt { irq } => self.handle_wait_for_interrupt(tid, pid, irq), + Syscall::AcknowledgeInterrupt { irq } => self.handle_ack_interrupt(pid, irq), + Syscall::WaitAny { sources } => self.handle_wait_any(tid, pid, sources), + } + } +} diff --git a/crates/kernel/src/syscall/simple.rs b/crates/kernel/src/syscall/simple.rs new file mode 100644 index 0000000..3f7aef9 --- /dev/null +++ b/crates/kernel/src/syscall/simple.rs @@ -0,0 +1,94 @@ +use platform::Platform; + +use crate::{Kernel, SyscallOutcome, SyscallResult}; + +impl Kernel

{ + pub(crate) fn handle_get_time(&self) -> SyscallOutcome { + let ticks = self.platform.current_ticks(); + SyscallOutcome::Complete(SyscallResult::Value(ticks.raw() as usize)) + } + + pub(crate) fn handle_get_process_id(&self, pid: types::id::ProcessId) -> SyscallOutcome { + SyscallOutcome::Complete(SyscallResult::Value(pid.raw() as usize)) + } + + pub(crate) fn handle_yield(&mut self, caller_tid: types::id::ThreadId) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + let core = self.platform.core_id(); + let thread = match self.threads.get_mut(caller_tid) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + let priority = thread.priority(); + if thread.preempt().is_err() { + return SyscallOutcome::Complete(SyscallResult::Ok); + } + + self.scheduler.add(caller_tid, priority, core); + + match self.scheduler.next(core) { + Some(next) if next != caller_tid => { + if self.threads.get_mut(next).and_then(|t| t.set_running(core)).is_ok() { + SyscallOutcome::SwitchTo(next) + } else { + let _ = self.threads.get_mut(caller_tid).map(|t| t.set_running(core)); + SyscallOutcome::Complete(SyscallResult::Ok) + } + } + _ => { + let _ = self.threads.get_mut(caller_tid).map(|t| t.set_running(core)); + SyscallOutcome::Complete(SyscallResult::Ok) + } + } + } + + pub(crate) fn handle_exit( + &mut self, + _caller_tid: types::id::ThreadId, + pid: types::id::ProcessId, + ) -> SyscallOutcome { + let thread_ids: [Option; 4] = { + match self.processes.get(pid) { + Ok(p) => { + let mut ids = [None; 4]; + for (i, tid) in p.thread_ids().enumerate() { + if i < 4 { + ids[i] = Some(tid); + } + } + ids + } + Err(_) => return SyscallOutcome::TerminateCaller, + } + }; + + self.cleanup_threads(&thread_ids); + + let _ = self.processes.destroy(pid); + self.process_memory[pid.raw() as usize] = None; + SyscallOutcome::TerminateCaller + } + + pub(crate) fn handle_exit_thread( + &mut self, + caller_tid: types::id::ThreadId, + pid: types::id::ProcessId, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + self.scheduler.remove(caller_tid); + let _ = self.threads.destroy(caller_tid); + + if let Ok(process) = self.processes.get_mut(pid) { + process.remove_thread(caller_tid); + if process.thread_ids().count() == 0 { + let _ = self.processes.destroy(pid); + self.process_memory[pid.raw() as usize] = None; + } + } + + SyscallOutcome::TerminateCaller + } +}