153 lines
4.5 KiB
Rust
153 lines
4.5 KiB
Rust
//! Budgets: carved from the creator, enforced, and returned on death.
|
|
|
|
use super::helpers::CHILD_LIMITS;
|
|
use super::*;
|
|
use crate::{SyscallOutcome, SyscallResult};
|
|
use domain::budget::Limits;
|
|
use domain::error::KernelError;
|
|
use domain::syscall::Syscall;
|
|
use types::address::DataRef;
|
|
use types::bounded::{CapSlot, PageCount};
|
|
|
|
fn create(k: &mut Kernel<MockPlatform>, limits: Limits) -> SyscallResult {
|
|
match k.handle_syscall(Syscall::CreateProcess {
|
|
create_cap: CapSlot::new(0).unwrap(),
|
|
image: DataRef::empty(),
|
|
limits,
|
|
}) {
|
|
SyscallOutcome::Complete(r) => r,
|
|
_ => panic!("create process blocked"),
|
|
}
|
|
}
|
|
|
|
fn minus(a: Limits, b: Limits) -> Limits {
|
|
Limits {
|
|
pages: a.pages - b.pages,
|
|
endpoints: a.endpoints - b.endpoints,
|
|
notifications: a.notifications - b.notifications,
|
|
regions: a.regions - b.regions,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn init_starts_with_the_whole_pool() {
|
|
let (k, init) = kernel_with_process();
|
|
let l = k.limits_of(init).unwrap();
|
|
assert_eq!(l.pages, k.free_page_count() + k.page_usage(init));
|
|
assert_eq!(l.endpoints, domain::endpoint::MAX_ENDPOINTS);
|
|
}
|
|
|
|
#[test]
|
|
fn a_child_is_carved_from_its_creator() {
|
|
let (mut k, init) = kernel_with_process();
|
|
let before = k.limits_of(init).unwrap();
|
|
let SyscallResult::Slot(_) = create(&mut k, CHILD_LIMITS) else {
|
|
panic!("create failed");
|
|
};
|
|
assert_eq!(k.limits_of(init).unwrap(), minus(before, CHILD_LIMITS));
|
|
}
|
|
|
|
#[test]
|
|
fn a_child_gets_no_more_than_its_creator_has_spare() {
|
|
let (mut k, init) = kernel_with_process();
|
|
let before = k.limits_of(init).unwrap();
|
|
// The creator's own pages are in use, so its whole limit is not spare.
|
|
let greedy = Limits {
|
|
pages: before.pages,
|
|
..CHILD_LIMITS
|
|
};
|
|
assert!(matches!(
|
|
create(&mut k, greedy),
|
|
SyscallResult::Error(KernelError::OutOfMemory)
|
|
));
|
|
let greedy = Limits {
|
|
endpoints: before.endpoints + 1,
|
|
..CHILD_LIMITS
|
|
};
|
|
assert!(matches!(
|
|
create(&mut k, greedy),
|
|
SyscallResult::Error(KernelError::OutOfMemory)
|
|
));
|
|
assert_eq!(k.limits_of(init).unwrap(), before);
|
|
}
|
|
|
|
#[test]
|
|
fn kernel_objects_count_against_the_owner() {
|
|
let (mut k, init) = kernel_with_process();
|
|
let one = Limits {
|
|
pages: 8,
|
|
endpoints: 1,
|
|
notifications: 1,
|
|
regions: 1,
|
|
};
|
|
let child = k.create_process_internal(init, one).unwrap();
|
|
assert!(k.create_endpoint(child).is_ok());
|
|
assert!(matches!(
|
|
k.create_endpoint(child),
|
|
Err(KernelError::NoFreeSlot)
|
|
));
|
|
assert!(k.create_notification(child).is_ok());
|
|
assert!(matches!(
|
|
k.create_notification(child),
|
|
Err(KernelError::NoFreeSlot)
|
|
));
|
|
let page = PageCount::new(1).unwrap();
|
|
assert!(k.create_pool_region(child, page).is_ok());
|
|
assert!(matches!(
|
|
k.create_pool_region(child, page),
|
|
Err(KernelError::NoFreeSlot)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn a_failed_region_leaves_no_charge_behind() {
|
|
let (mut k, init) = kernel_with_process();
|
|
let one = Limits {
|
|
pages: 2,
|
|
endpoints: 0,
|
|
notifications: 0,
|
|
regions: 1,
|
|
};
|
|
let child = k.create_process_internal(init, one).unwrap();
|
|
// More pages than the child may hold: the region slot stays free.
|
|
assert!(matches!(
|
|
k.create_pool_region(child, PageCount::new(8).unwrap()),
|
|
Err(KernelError::OutOfMemory)
|
|
));
|
|
assert!(
|
|
k.create_pool_region(child, PageCount::new(1).unwrap())
|
|
.is_ok()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn death_returns_the_budget_to_the_parent() {
|
|
let (mut k, init) = kernel_with_process();
|
|
let before = k.limits_of(init).unwrap();
|
|
let child = k.create_process_internal(init, CHILD_LIMITS).unwrap();
|
|
k.create_endpoint(child).unwrap();
|
|
k.destroy_process(child);
|
|
assert_eq!(k.limits_of(init).unwrap(), before);
|
|
}
|
|
|
|
#[test]
|
|
fn an_orphan_returns_its_budget_to_the_grandparent() {
|
|
let (mut k, init) = kernel_with_process();
|
|
let before = k.limits_of(init).unwrap();
|
|
let a = k.create_process_internal(init, CHILD_LIMITS).unwrap();
|
|
let small = Limits {
|
|
pages: 4,
|
|
endpoints: 1,
|
|
notifications: 1,
|
|
regions: 1,
|
|
};
|
|
let b = k.create_process_internal(a, small).unwrap();
|
|
k.destroy_process(a);
|
|
assert_eq!(k.limits_of(init).unwrap(), minus(before, small));
|
|
// a's slot may be reused; b's budget must still find its way home.
|
|
let c = k.create_process_internal(init, CHILD_LIMITS).unwrap();
|
|
k.destroy_process(b);
|
|
k.destroy_process(c);
|
|
assert_eq!(k.limits_of(init).unwrap(), before);
|
|
}
|