Implement domain and endpoint modules with capability management, message passing, and scheduling features
This commit is contained in:
33
crates/domain/src/capability/authority.rs
Normal file
33
crates/domain/src/capability/authority.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use types::{
|
||||
bounded::CapSlot,
|
||||
id::ProcessId,
|
||||
permission::{Permission, PermissionSet},
|
||||
};
|
||||
|
||||
use crate::{capability::Capability, error::KernelError, resource::Resource};
|
||||
|
||||
pub trait CapabilityAuthority {
|
||||
fn grant(
|
||||
&mut self,
|
||||
to: ProcessId,
|
||||
resource: Resource,
|
||||
permissions: PermissionSet,
|
||||
) -> Result<CapSlot, KernelError>;
|
||||
|
||||
fn revoke(&mut self, owner: ProcessId, slot: CapSlot) -> Result<(), KernelError>;
|
||||
|
||||
fn delegate(
|
||||
&mut self,
|
||||
from: ProcessId,
|
||||
slot: CapSlot,
|
||||
to: ProcessId,
|
||||
restrict: PermissionSet,
|
||||
) -> Result<CapSlot, KernelError>;
|
||||
|
||||
fn check(
|
||||
&self,
|
||||
caller: ProcessId,
|
||||
slot: CapSlot,
|
||||
needed: Permission,
|
||||
) -> Result<&Capability, KernelError>;
|
||||
}
|
||||
82
crates/domain/src/capability/mod.rs
Normal file
82
crates/domain/src/capability/mod.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use types::{bounded::CapSlot, generation::Generation, permission::PermissionSet};
|
||||
|
||||
pub mod authority;
|
||||
|
||||
use crate::{error::KernelError, resource::Resource};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "tests.rs"]
|
||||
mod tests;
|
||||
|
||||
const MAX_CAPS_PER_PROCESS: usize = 16;
|
||||
|
||||
pub struct Capability {
|
||||
resource: Resource,
|
||||
permissions: PermissionSet,
|
||||
generation: Generation,
|
||||
}
|
||||
|
||||
impl Capability {
|
||||
pub(crate) fn new(
|
||||
resource: Resource,
|
||||
permissions: PermissionSet,
|
||||
generation: Generation,
|
||||
) -> Self {
|
||||
Self {
|
||||
resource,
|
||||
permissions,
|
||||
generation,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resource(&self) -> &Resource {
|
||||
&self.resource
|
||||
}
|
||||
|
||||
pub fn permissions(&self) -> &PermissionSet {
|
||||
&self.permissions
|
||||
}
|
||||
|
||||
pub fn generation(&self) -> &Generation {
|
||||
&self.generation
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CapabilityTable {
|
||||
slots: [Option<Capability>; MAX_CAPS_PER_PROCESS],
|
||||
}
|
||||
|
||||
impl CapabilityTable {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
slots: [const { None }; MAX_CAPS_PER_PROCESS],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, slot: CapSlot) -> Result<&Capability, KernelError> {
|
||||
let idx = slot.value() as usize;
|
||||
self.slots[idx]
|
||||
.as_ref()
|
||||
.ok_or(KernelError::InvalidSlot(slot))
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, cap: Capability) -> Result<CapSlot, KernelError> {
|
||||
let idx = self
|
||||
.slots
|
||||
.iter()
|
||||
.position(|s| s.is_none())
|
||||
.ok_or(KernelError::NoFreeSlot)?;
|
||||
|
||||
self.slots[idx] = Some(cap);
|
||||
CapSlot::new(idx as u8).ok_or(KernelError::NoFreeSlot)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, slot: CapSlot) -> Result<(), KernelError> {
|
||||
let idx = slot.value() as usize;
|
||||
if self.slots[idx].is_none() {
|
||||
return Err(KernelError::InvalidSlot(slot));
|
||||
}
|
||||
self.slots[idx] = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
152
crates/domain/src/capability/tests.rs
Normal file
152
crates/domain/src/capability/tests.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use super::*;
|
||||
use types::generation::Generation;
|
||||
use types::id::{EndpointId, RegionId};
|
||||
use types::permission::{Permission, PermissionSet};
|
||||
|
||||
fn perms(permissions: &[Permission]) -> PermissionSet {
|
||||
permissions
|
||||
.iter()
|
||||
.fold(PermissionSet::empty(), |set, &p| set.with(p))
|
||||
}
|
||||
|
||||
fn endpoint_cap(id: u16, permissions: &[Permission]) -> Capability {
|
||||
Capability::new(
|
||||
Resource::Endpoint(EndpointId::from_raw(id)),
|
||||
perms(permissions),
|
||||
Generation::initial(),
|
||||
)
|
||||
}
|
||||
|
||||
fn memory_cap(id: u16, permissions: &[Permission]) -> Capability {
|
||||
Capability::new(
|
||||
Resource::Memory(RegionId::from_raw(id)),
|
||||
perms(permissions),
|
||||
Generation::initial(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_table_is_empty() {
|
||||
let table = CapabilityTable::new();
|
||||
let slot = CapSlot::new(0).unwrap();
|
||||
assert!(table.get(slot).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_and_get() {
|
||||
let mut table = CapabilityTable::new();
|
||||
let slot = table.insert(endpoint_cap(1, &[Permission::Read])).unwrap();
|
||||
let retrieved = table.get(slot).unwrap();
|
||||
|
||||
assert!(retrieved.permissions().allows(Permission::Read));
|
||||
assert!(!retrieved.permissions().allows(Permission::Write));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_then_get_fails() {
|
||||
let mut table = CapabilityTable::new();
|
||||
let slot = table.insert(endpoint_cap(1, &[Permission::Read])).unwrap();
|
||||
table.remove(slot).unwrap();
|
||||
assert!(table.get(slot).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_invalid_slot_fails() {
|
||||
let mut table = CapabilityTable::new();
|
||||
let slot = CapSlot::new(5).unwrap();
|
||||
|
||||
assert!(table.remove(slot).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_reuses_removed_slot() {
|
||||
let mut table = CapabilityTable::new();
|
||||
let slot = table.insert(endpoint_cap(1, &[Permission::Read])).unwrap();
|
||||
table.remove(slot).unwrap();
|
||||
let new_slot = table.insert(endpoint_cap(2, &[Permission::Write])).unwrap();
|
||||
assert_eq!(slot, new_slot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_table_returns_no_free_slot() {
|
||||
let mut table = CapabilityTable::new();
|
||||
for i in 0..MAX_CAPS_PER_PROCESS {
|
||||
assert!(
|
||||
table
|
||||
.insert(endpoint_cap(i as u16, &[Permission::Read]))
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
assert!(table.insert(endpoint_cap(999, &[])).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generation_mismatch_detected() {
|
||||
let gen_v1 = Generation::initial();
|
||||
let gen_v2 = gen_v1.next();
|
||||
|
||||
assert!(gen_v1.is_valid_against(&gen_v1));
|
||||
assert!(!gen_v1.is_valid_against(&gen_v2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restrict_to_never_escalates() {
|
||||
let full = perms(&[Permission::Read, Permission::Write, Permission::Execute]);
|
||||
let read_only = perms(&[Permission::Read]);
|
||||
|
||||
let restricted = full.restrict_to(&read_only);
|
||||
|
||||
assert!(restricted.allows(Permission::Read));
|
||||
assert!(!restricted.allows(Permission::Write));
|
||||
assert!(!restricted.allows(Permission::Execute));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restrict_to_cannot_add_permissions() {
|
||||
let read_only = perms(&[Permission::Read]);
|
||||
let wants_everything = perms(&[
|
||||
Permission::Read,
|
||||
Permission::Write,
|
||||
Permission::Execute,
|
||||
Permission::Delegate,
|
||||
Permission::Revoke,
|
||||
]);
|
||||
|
||||
let result = read_only.restrict_to(&wants_everything);
|
||||
|
||||
assert!(result.allows(Permission::Read));
|
||||
assert!(!result.allows(Permission::Write));
|
||||
assert!(!result.allows(Permission::Execute));
|
||||
assert!(!result.allows(Permission::Delegate));
|
||||
assert!(!result.allows(Permission::Revoke));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_subset_of() {
|
||||
let rw = perms(&[Permission::Read, Permission::Write]);
|
||||
let read_only = perms(&[Permission::Read]);
|
||||
|
||||
assert!(read_only.is_subset_of(&rw));
|
||||
assert!(!rw.is_subset_of(&read_only));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_permission_set_allows_nothing() {
|
||||
let empty = PermissionSet::empty();
|
||||
|
||||
assert!(!empty.allows(Permission::Read));
|
||||
assert!(!empty.allows(Permission::Write));
|
||||
assert!(!empty.allows(Permission::Execute));
|
||||
assert!(!empty.allows(Permission::Delegate));
|
||||
assert!(!empty.allows(Permission::Revoke));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capability_preserves_resource_and_permissions() {
|
||||
let permissions = [Permission::Read, Permission::Write];
|
||||
let expected_permissions = perms(&permissions);
|
||||
let cap = memory_cap(42, &permissions);
|
||||
|
||||
assert_eq!(cap.permissions(), &expected_permissions);
|
||||
assert_eq!(cap.generation(), &Generation::initial());
|
||||
}
|
||||
Reference in New Issue
Block a user