feat(kernel): add RequestPages and ReleasePages syscalls
This commit is contained in:
@@ -125,6 +125,10 @@ impl<P: Platform> Kernel<P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn free_page_count(&self) -> usize {
|
||||||
|
self.page_pool.free_count()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn process_exists(&self, pid: ProcessId) -> bool {
|
pub fn process_exists(&self, pid: ProcessId) -> bool {
|
||||||
self.processes.get(pid).is_ok()
|
self.processes.get(pid).is_ok()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ impl<P: Platform> Kernel<P> {
|
|||||||
Syscall::Yield => self.handle_yield(tid),
|
Syscall::Yield => self.handle_yield(tid),
|
||||||
Syscall::Exit => self.handle_exit(tid, pid),
|
Syscall::Exit => self.handle_exit(tid, pid),
|
||||||
Syscall::ExitThread => self.handle_exit_thread(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(
|
_ => SyscallOutcome::Complete(SyscallResult::Error(
|
||||||
domain::error::KernelError::WouldBlock,
|
domain::error::KernelError::WouldBlock,
|
||||||
)),
|
)),
|
||||||
@@ -110,4 +112,80 @@ impl<P: Platform> Kernel<P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
crates/kernel/src/tests/memory.rs
Normal file
49
crates/kernel/src/tests/memory.rs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
mod exit;
|
mod exit;
|
||||||
|
mod memory;
|
||||||
mod simple;
|
mod simple;
|
||||||
mod tick;
|
mod tick;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,42 @@
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct EntryPoint(usize);
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct StackPointer(usize);
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct VirtualAddress(usize);
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct PhysicalAddress(usize);
|
pub struct PhysicalAddress(usize);
|
||||||
|
|
||||||
@@ -29,3 +59,12 @@ pub struct DataRef {
|
|||||||
addr: VirtualAddress,
|
addr: VirtualAddress,
|
||||||
len: usize,
|
len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DataRef {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
addr: VirtualAddress::from_raw(0),
|
||||||
|
len: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user