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,11 +1,9 @@
|
||||
use domain::memory::MemoryRegion;
|
||||
use domain::process::MAX_PROCESSES;
|
||||
use platform::Platform;
|
||||
use platform::{FrameSource, MapError, Platform};
|
||||
use types::{
|
||||
address::{EntryPoint, PhysicalAddress, StackPointer, VirtualAddress},
|
||||
bounded::PageCount,
|
||||
id::{CoreId, IrqId, ProcessId},
|
||||
permission::{Permission, PermissionSet},
|
||||
mapping::PagePerms,
|
||||
time::{Ticks, TimerDuration},
|
||||
};
|
||||
|
||||
@@ -17,6 +15,7 @@ use crate::trap;
|
||||
pub struct RiscVPlatform {
|
||||
num_cores: u8,
|
||||
kernel_pt: usize,
|
||||
/// Root table of each process's address space, 0 if it has none.
|
||||
page_tables: [usize; MAX_PROCESSES],
|
||||
}
|
||||
|
||||
@@ -33,40 +32,76 @@ impl RiscVPlatform {
|
||||
self.page_tables[pid.raw() as usize]
|
||||
}
|
||||
|
||||
pub fn set_page_table(&mut self, pid: ProcessId, addr: usize) {
|
||||
self.page_tables[pid.raw() as usize] = addr;
|
||||
}
|
||||
|
||||
fn ensure_page_table(&mut self, pid: ProcessId) -> &mut mmu::PageTable {
|
||||
let idx = pid.raw() as usize;
|
||||
if self.page_tables[idx] == 0 {
|
||||
let kernel_pt = unsafe { &*(self.kernel_pt as *const mmu::PageTable) };
|
||||
let user_pt = mmu::create_user_page_table(
|
||||
kernel_pt,
|
||||
PhysicalAddress::from_raw(0),
|
||||
0,
|
||||
VirtualAddress::from_raw(0x10000),
|
||||
);
|
||||
self.page_tables[idx] = user_pt as *const mmu::PageTable as usize;
|
||||
fn root(&self, pid: ProcessId) -> Option<usize> {
|
||||
match self.page_tables.get(pid.raw() as usize) {
|
||||
Some(&0) | None => None,
|
||||
Some(&root) => Some(root),
|
||||
}
|
||||
unsafe { &mut *(self.page_tables[idx] as *mut mmu::PageTable) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Platform for RiscVPlatform {
|
||||
fn configure_protection(&mut self, process: ProcessId, regions: &[MemoryRegion]) {
|
||||
let pt = self.ensure_page_table(process);
|
||||
for region in regions {
|
||||
mmu::map(
|
||||
pt,
|
||||
mmu::Mapping {
|
||||
va: VirtualAddress::from_raw(region.base().raw()),
|
||||
pa: region.base(),
|
||||
pages: region.size().value() as usize,
|
||||
flags: mmu::Sv39Flags::user(true, true, true),
|
||||
},
|
||||
);
|
||||
const USER_VA_START: usize = mmu::USER_VA_START;
|
||||
const USER_VA_END: usize = mmu::USER_VA_END;
|
||||
|
||||
fn create_address_space(
|
||||
&mut self,
|
||||
process: ProcessId,
|
||||
frames: &mut dyn FrameSource,
|
||||
) -> Result<(), MapError> {
|
||||
let slot = self
|
||||
.page_tables
|
||||
.get_mut(process.raw() as usize)
|
||||
.ok_or(MapError::NoAddressSpace)?;
|
||||
// SAFETY: the kernel page table is identity mapped and lives forever.
|
||||
let kernel_root = unsafe { &*(self.kernel_pt as *const mmu::PageTable) };
|
||||
*slot = mmu::create_user_root(kernel_root, frames)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy_address_space(&mut self, process: ProcessId) {
|
||||
let Some(root) = self.root(process) else {
|
||||
return;
|
||||
};
|
||||
// The caller may be tearing down the space we are running on; get
|
||||
// off it before the kernel frees its frames.
|
||||
if mmu::active_root() == root {
|
||||
// SAFETY: the kernel page table is identity mapped and lives forever.
|
||||
mmu::activate(unsafe { &*(self.kernel_pt as *const mmu::PageTable) });
|
||||
}
|
||||
self.page_tables[process.raw() as usize] = 0;
|
||||
// SAFETY: flushing translations has no other effect.
|
||||
unsafe { core::arch::asm!("sfence.vma zero, zero") };
|
||||
}
|
||||
|
||||
fn map_page(
|
||||
&mut self,
|
||||
process: ProcessId,
|
||||
va: VirtualAddress,
|
||||
pa: PhysicalAddress,
|
||||
perms: PagePerms,
|
||||
frames: &mut dyn FrameSource,
|
||||
) -> Result<(), MapError> {
|
||||
let root = self.root(process).ok_or(MapError::NoAddressSpace)?;
|
||||
mmu::map_user_page(
|
||||
root,
|
||||
va.raw(),
|
||||
pa.raw(),
|
||||
mmu::Sv39Flags::user(perms),
|
||||
frames,
|
||||
)
|
||||
}
|
||||
|
||||
fn unmap_page(&mut self, process: ProcessId, va: VirtualAddress) {
|
||||
if let Some(root) = self.root(process) {
|
||||
mmu::unmap_user_page(root, va.raw());
|
||||
}
|
||||
}
|
||||
|
||||
fn zero_frame(&mut self, pa: PhysicalAddress) {
|
||||
// SAFETY: pool frames are identity mapped kernel RAM and the kernel
|
||||
// only zeroes frames it has just allocated.
|
||||
unsafe { core::ptr::write_bytes(pa.raw() as *mut u8, 0, mmu::PAGE_SIZE) };
|
||||
}
|
||||
|
||||
fn enable_user_mode(&mut self, entry: EntryPoint, stack: StackPointer) -> ! {
|
||||
@@ -117,63 +152,10 @@ impl Platform for RiscVPlatform {
|
||||
self.num_cores
|
||||
}
|
||||
|
||||
fn map_shared(
|
||||
&mut self,
|
||||
process: ProcessId,
|
||||
base: PhysicalAddress,
|
||||
size: PageCount,
|
||||
permissions: PermissionSet,
|
||||
) {
|
||||
let pt = self.ensure_page_table(process);
|
||||
mmu::map(
|
||||
pt,
|
||||
mmu::Mapping {
|
||||
va: VirtualAddress::from_raw(base.raw()),
|
||||
pa: base,
|
||||
pages: size.value() as usize,
|
||||
flags: mmu::Sv39Flags::user(
|
||||
permissions.allows(Permission::Read),
|
||||
permissions.allows(Permission::Write),
|
||||
permissions.allows(Permission::Execute),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn debug_putchar(&self, byte: u8) {
|
||||
crate::hw::uart::Uart::new().put_char(byte);
|
||||
}
|
||||
|
||||
fn translate_va(&self, process: ProcessId, va: VirtualAddress) -> Option<PhysicalAddress> {
|
||||
let pt_addr = self.page_tables[process.raw() as usize];
|
||||
if pt_addr == 0 {
|
||||
return None;
|
||||
}
|
||||
let pt = unsafe { &*(pt_addr as *const mmu::PageTable) };
|
||||
mmu::translate(pt, va.raw()).map(PhysicalAddress::from_raw)
|
||||
}
|
||||
|
||||
fn map_user_page(
|
||||
&mut self,
|
||||
process: ProcessId,
|
||||
va: VirtualAddress,
|
||||
pa: PhysicalAddress,
|
||||
writable: bool,
|
||||
executable: bool,
|
||||
) {
|
||||
let pt = self.ensure_page_table(process);
|
||||
let flags = mmu::Sv39Flags::user(true, writable, executable);
|
||||
mmu::map(
|
||||
pt,
|
||||
mmu::Mapping {
|
||||
va,
|
||||
pa,
|
||||
pages: 1,
|
||||
flags,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn halt(&self) -> ! {
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
|
||||
Reference in New Issue
Block a user