Files
k-os/crates/kernel/src/tests/helpers.rs
Gabriel Kaszewski 154599e3af feat(kernel): derivation-tree revocation, region caps
- caps record parent (mint/delegate/ipc), CapRef w/ pid+slot gens
- Revoke = delete subtree, keep self; drop/death re-parents children
- CreateRegion/MapRegion; mapping lives only as long as its cap
- drop Permission::Revoke + object-wide revoke
- mint any cap (badge only ep/ntfn), process caps delegable
- kani: descendants == subtree; model-based fuzz vs spec
2026-10-26 20:41:03 +01:00

162 lines
4.7 KiB
Rust

use domain::resource::Resource;
use domain::scheduler::Scheduler;
use domain::thread::ThreadState;
use platform::Platform;
use types::{
address::PhysicalAddress,
bounded::{CapSlot, Priority},
id::{EndpointId, ProcessId, ThreadId},
permission::PermissionSet,
time::TimeSlice,
};
use super::mock_platform::MockPlatform;
use crate::Kernel;
pub fn test_kernel() -> Kernel<MockPlatform> {
let mock = MockPlatform::new(1);
Kernel::new(mock, PhysicalAddress::from_raw(0x8400_0000), 1024)
}
pub fn kernel_with_process() -> (Kernel<MockPlatform>, ProcessId) {
let mut kernel = test_kernel();
let (pid, _tid) = kernel.boot(Priority::DEFAULT, TimeSlice::DEFAULT).unwrap();
(kernel, pid)
}
impl Kernel<MockPlatform> {
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 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)
&& 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)
&& matches!(t.state(), ThreadState::Ready)
&& self.scheduler.run_now(core, tid)
{
let _ = self.threads.get_mut(tid).map(|t| t.set_running(core));
}
}
pub fn create_endpoint_for_test(&mut self) -> EndpointId {
self.endpoints
.create(ProcessId::from_raw(0))
.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 fn endpoint_of_cap_for_test(&self, pid: ProcessId, slot: CapSlot) -> EndpointId {
match self
.processes
.get(pid)
.unwrap()
.capabilities()
.get(slot)
.unwrap()
.resource()
{
Resource::Endpoint(id) => *id,
_ => panic!("not an endpoint cap"),
}
}
/// The process a Process cap in init's table points at.
pub fn process_of_cap_for_test(&self, slot: CapSlot) -> ProcessId {
let init = ProcessId::from_raw(0);
match self
.processes
.get(init)
.unwrap()
.capabilities()
.get(slot)
.unwrap()
.resource()
{
Resource::Process(pid) => *pid,
_ => panic!("not a process cap"),
}
}
pub fn destroy_process_notifications_for_test(&mut self, owner: ProcessId) {
self.destroy_notifications_owned_by(owner);
}
/// The process a Process cap in `pid`'s table names, if it is one.
pub fn process_cap_target_for_test(&self, pid: ProcessId, slot: CapSlot) -> Option<ProcessId> {
match self
.processes
.get(pid)
.ok()?
.capabilities()
.get(slot)
.ok()?
.resource()
{
Resource::Process(target) if self.process_exists(*target) => Some(*target),
_ => None,
}
}
/// Which slots of `pid`'s capability table are occupied.
pub fn cap_slots_for_test(&self, pid: ProcessId) -> [bool; 64] {
let mut used = [false; 64];
if let Ok(p) = self.processes.get(pid) {
for (slot, _) in p.capabilities().iter() {
used[slot.value() as usize] = true;
}
}
used
}
pub fn thread_state_for_test(&self, tid: ThreadId) -> Option<ThreadState> {
self.threads.get(tid).ok().map(|t| *t.state())
}
}