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

@@ -1,3 +1,84 @@
pub mod gpu_setup;
pub mod input_setup;
pub mod storage_setup;
use crate::hw::uart::Uart;
use types::address::{PhysicalAddress, VirtualAddress};
use types::bounded::PageCount;
use types::id::ProcessId;
use types::mapping::PagePerms;
// Boot-time device wiring. Everything here goes through the kernel's
// mapping API so the kernel knows about every page a driver can touch.
// P5 replaces these hardcoded grants with capabilities handed to init.
fn kern() -> &'static mut kernel::Kernel<crate::platform::RiscVPlatform> {
unsafe { &mut *crate::kernel_ptr() }
}
fn pages(uart: &Uart, n: usize) -> PageCount {
u16::try_from(n)
.ok()
.and_then(PageCount::new)
.unwrap_or_else(|| crate::halt(uart, "bad page count"))
}
/// Maps device MMIO at `pa` into `pid` at `va`.
pub(crate) fn map_mmio(uart: &Uart, pid: ProcessId, va: usize, pa: usize, n: usize) {
let id = kern()
.create_device_region(pid, PhysicalAddress::from_raw(pa), pages(uart, n))
.unwrap_or_else(|_| crate::halt(uart, "device region failed"));
kern()
.map_region(id, pid, VirtualAddress::from_raw(va), PagePerms::ReadWrite)
.unwrap_or_else(|_| crate::halt(uart, "device map failed"));
}
/// Physically contiguous, zeroed DMA memory for `owner`, mapped read-write
/// at `va`. Returns its physical base for programming the device.
pub(crate) fn dma_buffer(uart: &Uart, owner: ProcessId, va: usize, n: usize) -> usize {
let id = kern()
.create_pool_region(owner, pages(uart, n))
.unwrap_or_else(|_| crate::halt(uart, "DMA region failed"));
kern()
.map_region(
id,
owner,
VirtualAddress::from_raw(va),
PagePerms::ReadWrite,
)
.unwrap_or_else(|_| crate::halt(uart, "DMA map failed"));
kern()
.region_base(id)
.unwrap_or_else(|| crate::halt(uart, "DMA region vanished"))
.raw()
}
/// Shares a fresh region between `owner` and `other` at the same `va`.
pub(crate) fn shared_buffer(uart: &Uart, owner: ProcessId, other: ProcessId, va: usize, n: usize) {
let id = kern()
.create_pool_region(owner, pages(uart, n))
.unwrap_or_else(|_| crate::halt(uart, "shared region failed"));
for pid in [owner, other] {
kern()
.map_region(id, pid, VirtualAddress::from_raw(va), PagePerms::ReadWrite)
.unwrap_or_else(|_| crate::halt(uart, "shared map failed"));
}
}
/// Writes `words` into a fresh page mapped at `va` in `pid`, so a driver
/// can learn the physical addresses of its DMA buffers.
pub(crate) fn info_page(uart: &Uart, pid: ProcessId, va: usize, words: &[u64]) {
let va = VirtualAddress::from_raw(va);
kern()
.map_fresh_range(pid, va, 1, PagePerms::ReadOnly)
.unwrap_or_else(|_| crate::halt(uart, "info page failed"));
let pa = kern()
.user_page_frame(pid, va)
.unwrap_or_else(|| crate::halt(uart, "info page vanished"));
let info = pa.raw() as *mut u64;
for (i, &w) in words.iter().enumerate() {
// SAFETY: `pa` is a pool frame just mapped for `pid`, identity
// mapped in kernel space, and `words` fits in one page.
unsafe { core::ptr::write_volatile(info.add(i), w) };
}
}