feat(kernel): kernel-owned memory model
- FrameTable: per-frame owner/use, replaces PagePool + ProcessMemory - Quota per process, page tables charged to owner - Regions for shared mem + device MMIO, torn down w/ owner - Platform: create/destroy AS, map/unmap, zero_frame; drop translate_va - RequestPages/ReleasePages/MapPage(move) checked vs kernel records - user window 0x1000..32MiB, private pts by construction - boot loader + device setup go through kernel api - kernel built in place, boot allocator bounded - isolation tests, randomized invariant test, FrameTable kani proofs - fixes: inflated release, unmapped release, dst va unchecked, heap overflow, unbounded pt alloc, unzeroed frames, double free
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod interrupt;
|
||||
mod memory;
|
||||
#[cfg(kani)]
|
||||
pub mod proofs;
|
||||
pub mod syscall;
|
||||
@@ -16,7 +17,7 @@ use domain::{
|
||||
endpoint::EndpointTable,
|
||||
error::KernelError,
|
||||
irq::IrqRoutingTable,
|
||||
memory::{RegionTable, page_pool::PagePool, process_memory::ProcessMemory},
|
||||
memory::{RegionTable, frame::FrameTable, quota::Quota},
|
||||
message::Message,
|
||||
process::{MAX_PROCESSES, ProcessTable},
|
||||
resource::Resource,
|
||||
@@ -26,6 +27,7 @@ use domain::{
|
||||
};
|
||||
use platform::Platform;
|
||||
use types::{
|
||||
address::PhysicalAddress,
|
||||
bounded::{CapSlot, Priority},
|
||||
generation::Generation,
|
||||
id::{ProcessId, ThreadId},
|
||||
@@ -39,8 +41,8 @@ pub struct Kernel<P: Platform> {
|
||||
threads: ThreadTable,
|
||||
endpoints: EndpointTable,
|
||||
scheduler: MultiCoreScheduler,
|
||||
page_pool: PagePool,
|
||||
process_memory: [Option<ProcessMemory>; MAX_PROCESSES],
|
||||
frames: FrameTable,
|
||||
quotas: [Quota; MAX_PROCESSES],
|
||||
regions: RegionTable,
|
||||
irqs: IrqRoutingTable,
|
||||
pending_messages: [Option<Message>; MAX_THREADS],
|
||||
@@ -49,7 +51,7 @@ pub struct Kernel<P: Platform> {
|
||||
}
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
pub fn new(platform: P, page_pool: PagePool) -> Self {
|
||||
pub fn new(platform: P, pool_base: PhysicalAddress, pool_pages: usize) -> Self {
|
||||
let num_cores = platform.num_cores();
|
||||
Self {
|
||||
platform,
|
||||
@@ -57,8 +59,8 @@ impl<P: Platform> Kernel<P> {
|
||||
threads: ThreadTable::new(),
|
||||
endpoints: EndpointTable::new(),
|
||||
scheduler: MultiCoreScheduler::new(num_cores),
|
||||
page_pool,
|
||||
process_memory: [const { None }; MAX_PROCESSES],
|
||||
frames: FrameTable::new(pool_base, pool_pages),
|
||||
quotas: [Quota::default(); MAX_PROCESSES],
|
||||
regions: RegionTable::new(),
|
||||
irqs: IrqRoutingTable::new(),
|
||||
pending_messages: [const { None }; MAX_THREADS],
|
||||
@@ -67,18 +69,51 @@ impl<P: Platform> Kernel<P> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the kernel directly in `slot`, one table at a time, so the
|
||||
/// whole structure never has to fit on the (small) boot stack.
|
||||
///
|
||||
/// # Safety
|
||||
/// `slot` must be valid for writes and properly aligned. Any previous
|
||||
/// value is overwritten without being dropped.
|
||||
pub unsafe fn init_in_place(
|
||||
slot: *mut Self,
|
||||
platform: P,
|
||||
pool_base: PhysicalAddress,
|
||||
pool_pages: usize,
|
||||
) {
|
||||
use core::ptr::addr_of_mut;
|
||||
let num_cores = platform.num_cores();
|
||||
// SAFETY: the caller guarantees `slot` is valid and aligned; each
|
||||
// field is written exactly once.
|
||||
unsafe {
|
||||
addr_of_mut!((*slot).platform).write(platform);
|
||||
addr_of_mut!((*slot).processes).write(ProcessTable::new());
|
||||
addr_of_mut!((*slot).threads).write(ThreadTable::new());
|
||||
addr_of_mut!((*slot).endpoints).write(EndpointTable::new());
|
||||
addr_of_mut!((*slot).scheduler).write(MultiCoreScheduler::new(num_cores));
|
||||
addr_of_mut!((*slot).frames).write(FrameTable::new(pool_base, pool_pages));
|
||||
addr_of_mut!((*slot).quotas).write([Quota::default(); MAX_PROCESSES]);
|
||||
addr_of_mut!((*slot).regions).write(RegionTable::new());
|
||||
addr_of_mut!((*slot).irqs).write(IrqRoutingTable::new());
|
||||
addr_of_mut!((*slot).pending_messages).write([const { None }; MAX_THREADS]);
|
||||
addr_of_mut!((*slot).pending_results).write([const { None }; MAX_THREADS]);
|
||||
addr_of_mut!((*slot).wait_sets).write([const { None }; MAX_THREADS]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boot(
|
||||
&mut self,
|
||||
priority: Priority,
|
||||
time_slice: TimeSlice,
|
||||
) -> Result<(ProcessId, ThreadId), KernelError> {
|
||||
let pid = self.processes.create(CapabilityTable::new(), None)?;
|
||||
if let Err(e) = self.create_address_space(pid) {
|
||||
let _ = self.processes.destroy(pid);
|
||||
return Err(e);
|
||||
}
|
||||
let tid = self.threads.spawn(pid, priority, time_slice, None)?;
|
||||
self.processes.get_mut(pid)?.add_thread(tid)?;
|
||||
|
||||
self.process_memory[pid.raw() as usize] =
|
||||
Some(ProcessMemory::new(pid, ProcessMemory::DEFAULT_PAGE_LIMIT));
|
||||
|
||||
let cap = Capability::new(
|
||||
Resource::ProcessCreate,
|
||||
PermissionSet::empty().with(Permission::Execute),
|
||||
@@ -116,10 +151,6 @@ impl<P: Platform> Kernel<P> {
|
||||
self.threads.get_mut(tid)?.set_running(core)
|
||||
}
|
||||
|
||||
pub fn free_page_count(&self) -> usize {
|
||||
self.page_pool.free_count()
|
||||
}
|
||||
|
||||
pub fn process_exists(&self, pid: ProcessId) -> bool {
|
||||
self.processes.get(pid).is_ok()
|
||||
}
|
||||
@@ -155,11 +186,10 @@ impl<P: Platform> Kernel<P> {
|
||||
|
||||
pub fn create_process_internal(&mut self) -> Option<ProcessId> {
|
||||
let pid = self.processes.create(CapabilityTable::new(), None).ok()?;
|
||||
self.process_memory[pid.raw() as usize] =
|
||||
Some(domain::memory::process_memory::ProcessMemory::new(
|
||||
pid,
|
||||
ProcessMemory::DEFAULT_PAGE_LIMIT,
|
||||
));
|
||||
if self.create_address_space(pid).is_err() {
|
||||
let _ = self.processes.destroy(pid);
|
||||
return None;
|
||||
}
|
||||
Some(pid)
|
||||
}
|
||||
|
||||
@@ -245,12 +275,7 @@ impl<P: Platform> Kernel<P> {
|
||||
|
||||
self.cleanup_threads(&thread_ids);
|
||||
|
||||
// Free allocated pages back to pool
|
||||
if let Some(pm) = &self.process_memory[pid.raw() as usize] {
|
||||
for &(pa, count) in pm.allocations() {
|
||||
self.page_pool.free(pa, count);
|
||||
}
|
||||
}
|
||||
self.teardown_memory(pid);
|
||||
|
||||
// Unblock threads in other processes that were blocked waiting
|
||||
// on endpoints where this process's threads were the other side.
|
||||
@@ -260,7 +285,6 @@ impl<P: Platform> Kernel<P> {
|
||||
// when the endpoint is revoked.
|
||||
|
||||
let _ = self.processes.destroy(pid);
|
||||
self.process_memory[pid.raw() as usize] = None;
|
||||
}
|
||||
|
||||
pub(crate) fn cleanup_threads(&mut self, thread_ids: &[Option<ThreadId>]) {
|
||||
|
||||
Reference in New Issue
Block a user