- 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
162 lines
4.7 KiB
Rust
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())
|
|
}
|
|
}
|