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:
2026-10-26 19:40:50 +01:00
parent e931485ca7
commit 8d2c40cb1e
43 changed files with 2815 additions and 1172 deletions

View File

@@ -5,4 +5,3 @@ edition = "2024"
[dependencies]
types = { workspace = true }
domain = { workspace = true }

View File

@@ -1,16 +1,58 @@
#![no_std]
use domain::memory::MemoryRegion;
use types::{
address::{EntryPoint, PhysicalAddress, StackPointer, VirtualAddress},
bounded::PageCount,
id::{CoreId, IrqId, ProcessId},
permission::PermissionSet,
mapping::PagePerms,
time::{Ticks, TimerDuration},
};
/// Supplies frames for page tables while the platform builds an address
/// space. The kernel charges each frame to the address space's owner.
pub trait FrameSource {
fn alloc_table_frame(&mut self) -> Option<PhysicalAddress>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MapError {
/// The frame source refused to supply a page-table frame.
OutOfFrames,
/// The address is outside the user window or not page aligned.
InvalidAddress,
/// The process has no address space.
NoAddressSpace,
}
/// The kernel's contract with an architecture. The kernel decides what is
/// mapped where; the platform only applies those decisions to hardware.
pub trait Platform {
fn configure_protection(&mut self, process: ProcessId, regions: &[MemoryRegion]);
/// User mappings live in `[USER_VA_START, USER_VA_END)`. The platform
/// guarantees nothing else is mapped in that window in any address
/// space, so page tables covering it are private to each process.
const USER_VA_START: usize;
const USER_VA_END: usize;
fn create_address_space(
&mut self,
process: ProcessId,
frames: &mut dyn FrameSource,
) -> Result<(), MapError>;
/// Forgets the address space. The kernel frees its frames afterwards,
/// so the platform must stop using them before returning.
fn destroy_address_space(&mut self, process: ProcessId);
fn map_page(
&mut self,
process: ProcessId,
va: VirtualAddress,
pa: PhysicalAddress,
perms: PagePerms,
frames: &mut dyn FrameSource,
) -> Result<(), MapError>;
/// Removes the mapping at `va` and flushes it from the TLB.
fn unmap_page(&mut self, process: ProcessId, va: VirtualAddress);
/// Fills a frame with zeros before it is handed to a process.
fn zero_frame(&mut self, pa: PhysicalAddress);
fn enable_user_mode(&mut self, entry: EntryPoint, stack: StackPointer) -> !;
fn set_timer(&mut self, duration: TimerDuration);
fn current_ticks(&self) -> Ticks;
@@ -19,22 +61,6 @@ pub trait Platform {
fn send_ipi(&mut self, target: CoreId);
fn core_id(&self) -> CoreId;
fn num_cores(&self) -> u8;
fn map_shared(
&mut self,
process: ProcessId,
base: PhysicalAddress,
size: PageCount,
permissions: PermissionSet,
);
fn halt(&self) -> !;
fn debug_putchar(&self, byte: u8);
fn translate_va(&self, process: ProcessId, va: VirtualAddress) -> Option<PhysicalAddress>;
fn map_user_page(
&mut self,
process: ProcessId,
va: VirtualAddress,
pa: PhysicalAddress,
writable: bool,
executable: bool,
);
}