From 471d96b9a49f87ba39f14366fd1fe68934cab087 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 18 Oct 2026 00:19:24 +0200 Subject: [PATCH] feat(kernel): add RequestPages and ReleasePages syscalls --- crates/kernel/src/lib.rs | 4 ++ crates/kernel/src/syscall.rs | 78 +++++++++++++++++++++++++++++++ crates/kernel/src/tests/memory.rs | 49 +++++++++++++++++++ crates/kernel/src/tests/mod.rs | 1 + crates/types/src/address.rs | 39 ++++++++++++++++ 5 files changed, 171 insertions(+) create mode 100644 crates/kernel/src/tests/memory.rs diff --git a/crates/kernel/src/lib.rs b/crates/kernel/src/lib.rs index 12e886f..707377c 100644 --- a/crates/kernel/src/lib.rs +++ b/crates/kernel/src/lib.rs @@ -125,6 +125,10 @@ impl Kernel

{ } } + 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() } diff --git a/crates/kernel/src/syscall.rs b/crates/kernel/src/syscall.rs index ce18d93..237d8fd 100644 --- a/crates/kernel/src/syscall.rs +++ b/crates/kernel/src/syscall.rs @@ -16,6 +16,8 @@ impl Kernel

{ Syscall::Yield => self.handle_yield(tid), Syscall::Exit => self.handle_exit(tid, pid), Syscall::ExitThread => self.handle_exit_thread(tid, pid), + Syscall::RequestPages { count } => self.handle_request_pages(pid, count), + Syscall::ReleasePages { addr, count } => self.handle_release_pages(pid, addr, count), _ => SyscallOutcome::Complete(SyscallResult::Error( domain::error::KernelError::WouldBlock, )), @@ -110,4 +112,80 @@ impl Kernel

{ } } } + + fn handle_request_pages( + &mut self, + pid: types::id::ProcessId, + count: types::bounded::PageCount, + ) -> SyscallOutcome { + let pages = count.value() as usize; + + if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { + if pm.grant(pages).is_err() { + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::OutOfMemory, + )); + } + } else { + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::InvalidProcess(pid), + )); + } + + let addr = match self.page_pool.allocate(pages) { + Some(a) => a, + None => { + if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { + let _ = pm.release(pages); + } + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::OutOfMemory, + )); + } + }; + + let region = domain::memory::MemoryRegion::new( + types::id::RegionId::from_raw(0), + pid, + addr, + count, + types::generation::Generation::initial(), + ); + let _region_id = self.regions.insert(region); + + let slot = match self.grant_cap( + pid, + domain::resource::Resource::Memory(types::id::RegionId::from_raw(0)), + types::permission::PermissionSet::empty() + .with(types::permission::Permission::Read) + .with(types::permission::Permission::Write), + ) { + Ok(s) => s, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + SyscallOutcome::Complete(SyscallResult::Slot(slot)) + } + + fn handle_release_pages( + &mut self, + pid: types::id::ProcessId, + addr: types::address::VirtualAddress, + count: types::bounded::PageCount, + ) -> SyscallOutcome { + let pages = count.value() as usize; + let phys = types::address::PhysicalAddress::from_raw(addr.raw()); + + if !self.page_pool.free(phys, pages) { + return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::OutOfMemory, + )); + } + + if let Some(pm) = &mut self.process_memory[pid.raw() as usize] { + let _ = pm.release(pages); + } + + SyscallOutcome::Complete(SyscallResult::Ok) + } } diff --git a/crates/kernel/src/tests/memory.rs b/crates/kernel/src/tests/memory.rs new file mode 100644 index 0000000..011730f --- /dev/null +++ b/crates/kernel/src/tests/memory.rs @@ -0,0 +1,49 @@ +use super::*; +use crate::{SyscallOutcome, SyscallResult}; +use domain::syscall::Syscall; +use types::bounded::PageCount; +use types::address::VirtualAddress; + +#[test] +fn request_pages_allocates_and_grants_cap() { + let (mut k, _) = kernel_with_process(); + let outcome = k.handle_syscall(Syscall::RequestPages { + count: PageCount::new(4).unwrap(), + }); + assert!(matches!( + outcome, + SyscallOutcome::Complete(SyscallResult::Slot(_)) + )); +} + +#[test] +fn request_pages_reduces_pool() { + let (mut k, _) = kernel_with_process(); + let before = k.free_page_count(); + k.handle_syscall(Syscall::RequestPages { + count: PageCount::new(10).unwrap(), + }); + assert_eq!(k.free_page_count(), before - 10); +} + +#[test] +fn release_pages_frees_and_restores_pool() { + let (mut k, _) = kernel_with_process(); + let before = k.free_page_count(); + let slot = match k.handle_syscall(Syscall::RequestPages { + count: PageCount::new(4).unwrap(), + }) { + SyscallOutcome::Complete(SyscallResult::Slot(s)) => s, + _ => panic!("expected slot"), + }; + + let outcome = k.handle_syscall(Syscall::ReleasePages { + addr: VirtualAddress::from_raw(0x8000_0000), + count: PageCount::new(4).unwrap(), + }); + assert!(matches!( + outcome, + SyscallOutcome::Complete(SyscallResult::Ok) + )); + assert_eq!(k.free_page_count(), before); +} diff --git a/crates/kernel/src/tests/mod.rs b/crates/kernel/src/tests/mod.rs index df62df3..34841ec 100644 --- a/crates/kernel/src/tests/mod.rs +++ b/crates/kernel/src/tests/mod.rs @@ -1,4 +1,5 @@ mod exit; +mod memory; mod simple; mod tick; diff --git a/crates/types/src/address.rs b/crates/types/src/address.rs index 5ef3868..28d3724 100644 --- a/crates/types/src/address.rs +++ b/crates/types/src/address.rs @@ -1,12 +1,42 @@ #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct EntryPoint(usize); +impl EntryPoint { + pub fn from_raw(addr: usize) -> Self { + Self(addr) + } + + pub fn raw(&self) -> usize { + self.0 + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct StackPointer(usize); +impl StackPointer { + pub fn from_raw(addr: usize) -> Self { + Self(addr) + } + + pub fn raw(&self) -> usize { + self.0 + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct VirtualAddress(usize); +impl VirtualAddress { + pub fn from_raw(addr: usize) -> Self { + Self(addr) + } + + pub fn raw(&self) -> usize { + self.0 + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct PhysicalAddress(usize); @@ -29,3 +59,12 @@ pub struct DataRef { addr: VirtualAddress, len: usize, } + +impl DataRef { + pub fn empty() -> Self { + Self { + addr: VirtualAddress::from_raw(0), + len: 0, + } + } +}