diff --git a/crates/kernel/src/tests/helpers.rs b/crates/kernel/src/tests/helpers.rs new file mode 100644 index 0000000..b106fbf --- /dev/null +++ b/crates/kernel/src/tests/helpers.rs @@ -0,0 +1,100 @@ +use domain::memory::page_pool::PagePool; +use domain::resource::Resource; +use domain::scheduler::Scheduler; +use domain::thread::ThreadState; +use platform::Platform; +use types::{ + address::PhysicalAddress, + bounded::{CapSlot, Priority}, + id::{EndpointId, IrqId, ProcessId, ThreadId}, + permission::PermissionSet, + time::TimeSlice, +}; + +use crate::Kernel; +use super::mock_platform::MockPlatform; + +pub fn test_kernel() -> Kernel { + let mock = MockPlatform::new(1); + let pool = PagePool::new(PhysicalAddress::from_raw(0x8000_0000), 1024); + Kernel::new(mock, pool) +} + +pub fn kernel_with_process() -> (Kernel, ProcessId) { + let mut kernel = test_kernel(); + let (pid, _tid) = kernel + .boot(Priority::new(16).unwrap(), TimeSlice::new(100)) + .unwrap(); + (kernel, pid) +} + +impl Kernel { + pub fn spawn_thread_for_test( + &mut self, + pid: ProcessId, + priority: Priority, + time_slice: TimeSlice, + ) -> ThreadId { + let tid = self.threads.spawn(pid, priority, time_slice, None).expect("spawn"); + let _ = self.processes.get_mut(pid).map(|p| p.add_thread(tid)); + let core = self.platform.core_id(); + self.scheduler.add(tid, priority, core); + tid + } + + pub fn register_irq_for_test(&mut self, irq: IrqId, thread: ThreadId) { + let _ = self.irqs.register(irq, thread); + } + + pub fn block_thread_for_test( + &mut self, + tid: ThreadId, + reason: domain::thread::BlockReason, + ) { + let core = self.platform.core_id(); + if let Ok(t) = self.threads.get_mut(tid) { + if matches!(t.state(), ThreadState::Ready) { + let _ = t.set_running(core); + } + let _ = t.block(reason); + } + self.scheduler.remove(tid); + } + + pub fn switch_to_thread_for_test(&mut self, tid: ThreadId) { + 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)); + } + } + } + + pub fn create_endpoint_for_test(&mut self) -> EndpointId { + self.endpoints.create().expect("create endpoint") + } + + pub fn grant_cap_for_test( + &mut self, + pid: ProcessId, + resource: Resource, + permissions: PermissionSet, + ) -> CapSlot { + self.grant_cap(pid, resource, permissions).expect("grant cap") + } +} diff --git a/crates/kernel/src/tests/mock_platform.rs b/crates/kernel/src/tests/mock_platform.rs new file mode 100644 index 0000000..8ef02d9 --- /dev/null +++ b/crates/kernel/src/tests/mock_platform.rs @@ -0,0 +1,49 @@ +use domain::memory::MemoryRegion; +use domain::thread::SavedContext; +use platform::Platform; +use types::{ + address::{EntryPoint, StackPointer}, + id::{CoreId, IrqId, ProcessId}, + time::{Ticks, TimerDuration}, +}; + +pub struct MockPlatform { + ticks: Ticks, + core: CoreId, + cores: u8, +} + +impl MockPlatform { + pub fn new(num_cores: u8) -> Self { + Self { + ticks: Ticks::from_raw(0), + core: CoreId::from_raw(0), + cores: num_cores, + } + } + + pub fn set_ticks(&mut self, ticks: Ticks) { + self.ticks = ticks; + } +} + +impl Platform for MockPlatform { + fn configure_protection(&mut self, _process: ProcessId, _regions: &[MemoryRegion]) {} + fn context_switch(&mut self, _from: &mut SavedContext, _to: &SavedContext) {} + fn enable_user_mode(&mut self, _entry: EntryPoint, _stack: StackPointer) -> ! { + loop {} + } + fn set_timer(&mut self, _duration: TimerDuration) {} + fn current_ticks(&self) -> Ticks { + self.ticks + } + fn mask_irq(&mut self, _irq: IrqId) {} + fn unmask_irq(&mut self, _irq: IrqId) {} + fn send_ipi(&mut self, _target: CoreId) {} + fn core_id(&self) -> CoreId { + self.core + } + fn num_cores(&self) -> u8 { + self.cores + } +} diff --git a/crates/kernel/src/tests/mod.rs b/crates/kernel/src/tests/mod.rs index e81a84f..eec506d 100644 --- a/crates/kernel/src/tests/mod.rs +++ b/crates/kernel/src/tests/mod.rs @@ -1,3 +1,6 @@ +mod mock_platform; +mod helpers; + mod capability; mod exit; mod ipc; @@ -6,144 +9,12 @@ mod memory; mod simple; mod tick; -use domain::memory::page_pool::PagePool; -use domain::memory::MemoryRegion; -use domain::resource::Resource; -use domain::scheduler::Scheduler; -use domain::thread::{SavedContext, ThreadState}; -use platform::Platform; -use types::{ - address::{EntryPoint, PhysicalAddress, StackPointer}, - bounded::{CapSlot, Priority}, - id::{CoreId, EndpointId, IrqId, ProcessId, ThreadId}, - permission::PermissionSet, - time::{Ticks, TimerDuration, TimeSlice}, -}; +pub use mock_platform::MockPlatform; +pub use helpers::{test_kernel, kernel_with_process}; -use crate::{Kernel, SyscallResult}; - -pub struct MockPlatform { - ticks: Ticks, - core: CoreId, - cores: u8, -} - -impl MockPlatform { - pub fn new(num_cores: u8) -> Self { - Self { - ticks: Ticks::from_raw(0), - core: CoreId::from_raw(0), - cores: num_cores, - } - } - - pub fn set_ticks(&mut self, ticks: Ticks) { - self.ticks = ticks; - } -} - -impl Platform for MockPlatform { - fn configure_protection(&mut self, _process: ProcessId, _regions: &[MemoryRegion]) {} - fn context_switch(&mut self, _from: &mut SavedContext, _to: &SavedContext) {} - fn enable_user_mode(&mut self, _entry: EntryPoint, _stack: StackPointer) -> ! { - loop {} - } - fn set_timer(&mut self, _duration: TimerDuration) {} - fn current_ticks(&self) -> Ticks { - self.ticks - } - fn mask_irq(&mut self, _irq: IrqId) {} - fn unmask_irq(&mut self, _irq: IrqId) {} - fn send_ipi(&mut self, _target: CoreId) {} - fn core_id(&self) -> CoreId { - self.core - } - fn num_cores(&self) -> u8 { - self.cores - } -} - -pub fn test_kernel() -> Kernel { - let mock = MockPlatform::new(1); - let pool = PagePool::new(PhysicalAddress::from_raw(0x8000_0000), 1024); - Kernel::new(mock, pool) -} - -pub fn kernel_with_process() -> (Kernel, ProcessId) { - let mut kernel = test_kernel(); - let (pid, _tid) = kernel - .boot(Priority::new(16).unwrap(), TimeSlice::new(100)) - .unwrap(); - (kernel, pid) -} - -impl Kernel { - pub fn spawn_thread_for_test( - &mut self, - pid: ProcessId, - priority: Priority, - time_slice: TimeSlice, - ) -> ThreadId { - let tid = self.threads.spawn(pid, priority, time_slice, None).expect("spawn"); - let _ = self.processes.get_mut(pid).map(|p| p.add_thread(tid)); - let core = self.platform.core_id(); - self.scheduler.add(tid, priority, core); - tid - } - - pub fn register_irq_for_test(&mut self, irq: IrqId, thread: ThreadId) { - let _ = self.irqs.register(irq, thread); - } - - pub fn block_thread_for_test( - &mut self, - tid: ThreadId, - reason: domain::thread::BlockReason, - ) { - let core = self.platform.core_id(); - if let Ok(t) = self.threads.get_mut(tid) { - if matches!(t.state(), ThreadState::Ready) { - let _ = t.set_running(core); - } - let _ = t.block(reason); - } - self.scheduler.remove(tid); - } - - pub fn switch_to_thread_for_test(&mut self, tid: ThreadId) { - 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)); - } - } - } - - pub fn create_endpoint_for_test(&mut self) -> EndpointId { - self.endpoints.create().expect("create endpoint") - } - - pub fn grant_cap_for_test( - &mut self, - pid: ProcessId, - resource: Resource, - permissions: PermissionSet, - ) -> CapSlot { - self.grant_cap(pid, resource, permissions).expect("grant cap") - } -} +pub use crate::Kernel; +pub use types::address::PhysicalAddress; +pub use types::bounded::Priority; +pub use types::id::ProcessId; +pub use types::time::TimeSlice; +pub use domain::memory::page_pool::PagePool;