feat(kernel): object lifetimes via slot generations

- SlotGenerations per table, never wraps, retires exhausted slots
- caps to dead/reused process, endpoint, region rejected
- endpoints owned, die w/ owner, waiters woken w/ error
- DropCap syscall + libkos wrapper
- irq table bounds-checked, routes dropped w/ thread
- self-kill terminates caller, exit_thread full cleanup
- sched run_now: switch to named thread, not queue head
- lifetime tests, fuzz kills/recreates, kani gen proofs
This commit is contained in:
2026-10-26 19:54:23 +01:00
parent 8d2c40cb1e
commit d43421e33e
32 changed files with 790 additions and 207 deletions

View File

@@ -1,3 +1,4 @@
use crate::generations::SlotGenerations;
use types::{
address::{PhysicalAddress, VirtualAddress},
bounded::PageCount,
@@ -43,7 +44,6 @@ pub struct Region {
backing: RegionBacking,
base: PhysicalAddress,
pages: PageCount,
generation: Generation,
mappings: [Option<RegionMapping>; MAX_REGION_MAPPINGS],
}
@@ -59,7 +59,6 @@ impl Region {
backing,
base,
pages,
generation: Generation::initial(),
mappings: [None; MAX_REGION_MAPPINGS],
}
}
@@ -80,14 +79,6 @@ impl Region {
self.pages
}
pub fn generation(&self) -> &Generation {
&self.generation
}
pub fn revoke(&mut self) {
self.generation = self.generation.next();
}
pub fn mappings(&self) -> impl Iterator<Item = &RegionMapping> {
self.mappings.iter().flatten()
}
@@ -124,23 +115,42 @@ impl Region {
pub struct RegionTable {
regions: [Option<Region>; MAX_REGIONS],
generations: SlotGenerations<MAX_REGIONS>,
}
impl RegionTable {
pub fn new() -> Self {
Self {
regions: [const { None }; MAX_REGIONS],
generations: SlotGenerations::new(),
}
}
/// Generation capabilities to a live region must carry.
pub fn generation(&self, id: RegionId) -> Option<Generation> {
self.get(id)?;
self.generations.current(id.raw() as usize)
}
/// Invalidates every existing capability to `id`; the region lives on.
pub fn revoke(&mut self, id: RegionId) {
if self.get(id).is_some() {
self.generations.advance(id.raw() as usize);
}
}
fn free_slot(&self) -> Option<usize> {
(0..MAX_REGIONS).find(|&i| self.regions[i].is_none() && self.generations.is_usable(i))
}
/// The id the next `insert` will return.
pub fn next_free_id(&self) -> Option<RegionId> {
let idx = self.regions.iter().position(|s| s.is_none())?;
let idx = self.free_slot()?;
Some(RegionId::from_raw(idx as u16))
}
pub fn insert(&mut self, region: Region) -> Option<RegionId> {
let idx = self.regions.iter().position(|s| s.is_none())?;
let idx = self.free_slot()?;
self.regions[idx] = Some(region);
Some(RegionId::from_raw(idx as u16))
}
@@ -153,8 +163,12 @@ impl RegionTable {
self.regions.get_mut(id.raw() as usize)?.as_mut()
}
/// Removes the region; capabilities to it become invalid even if the
/// slot is later reused.
pub fn remove(&mut self, id: RegionId) -> Option<Region> {
self.regions.get_mut(id.raw() as usize)?.take()
let region = self.regions.get_mut(id.raw() as usize)?.take()?;
self.generations.advance(id.raw() as usize);
Some(region)
}
pub fn ids_owned_by(&self, owner: ProcessId) -> impl Iterator<Item = RegionId> + '_ {