Refactor: move all caps to their appropriate modules and files

This commit is contained in:
Berkus Decker 2020-12-13 20:57:46 +02:00
parent 57ae94847d
commit caa1929a0f
25 changed files with 1297 additions and 815 deletions

View File

@ -1,815 +0,0 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
//! Implementation of seL4-like capabilities.
// DerivationTree nodes record the tree of inheritance for caps:
// See the picture on derivation from seL4 manual for how this works: each cap contains a ref to
// DerivationTree node, which records the previous cap and the following cap(s).
// ☐ Rust implementation of capabilities - ?
// ☐ Need to implement in kernel entries storage and lookup
// ☐ cte = cap table entry (a cap_t plus mdb_node_t)
// ☐ mdb = ? (mdb_node_new)
// ☐ sameObjectAs()
// cap_get_capType();//generated
// lookupCapAndSlot();
// cap_domain_cap_new() etc //generated
// create_mapped_it_frame_cap(); //vspace.c
// pptr_of_cap(); -- extracts cap.pptr from cnode_cap
// deriveCap();
use {
crate::memory::PhysAddr,
core::{convert::TryFrom, fmt},
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
snafu::Snafu,
};
//==================
// Caps definitions
//==================
register_bitfields! {
u128,
NullCap [
Type OFFSET(64) NUMBITS(5) [
value = 0
]
],
// The combination of freeIndex and blockSize must match up with the
// definitions of MIN_SIZE_BITS and MAX_SIZE_BITS
// -- https://github.com/seL4/seL4/blob/master/include/object/structures_32.bf#L18
//
// /* It is assumed that every untyped is within seL4_MinUntypedBits and seL4_MaxUntypedBits
// * (inclusive). This means that every untyped stored as seL4_MinUntypedBits
// * subtracted from its size before it is stored in capBlockSize, and
// * capFreeIndex counts in chunks of size 2^seL4_MinUntypedBits. The seL4_MaxUntypedBits
// * is the minimal untyped that can be stored when considering both how
// * many bits of capBlockSize there are, and the largest offset that can
// * be stored in capFreeIndex */
// +#define MAX_FREE_INDEX(sizeBits) (BIT( (sizeBits) - seL4_MinUntypedBits ))
// +#define FREE_INDEX_TO_OFFSET(freeIndex) ((freeIndex)<<seL4_MinUntypedBits)
// #define GET_FREE_REF(base,freeIndex) ((word_t)(((word_t)(base)) + FREE_INDEX_TO_OFFSET(freeIndex)))
// #define GET_FREE_INDEX(base,free) (((word_t)(free) - (word_t)(base))>>seL4_MinUntypedBits)
// #define GET_OFFSET_FREE_PTR(base, offset) ((void *)(((word_t)(base)) + (offset)))
// +#define OFFSET_TO_FREE_INDEX(offset) ((offset)>>seL4_MinUntypedBits)
//
// exception_t decodeUntypedInvocation(word_t invLabel, word_t length,
// cte_t *slot, cap_t cap,
// extra_caps_t excaps, bool_t call,
// word_t *buffer);
// exception_t invokeUntyped_Retype(cte_t *srcSlot, bool_t reset,
// void *retypeBase, object_t newType,
// word_t userSize, slot_range_t destSlots,
// bool_t deviceMemory);
// // -- https://github.com/seL4/seL4/blob/master/src/object/untyped.c#L276
// -- https://github.com/seL4/seL4/blob/master/include/object/untyped.h
//
// /* Untyped size limits */
// #define seL4_MinUntypedBits 4
// #define seL4_MaxUntypedBits 47
// -- https://github.com/seL4/seL4/blob/master/libsel4/sel4_arch_include/aarch64/sel4/sel4_arch/constants.h#L234
//
// /*
// * Determine where in the Untyped region we should start allocating new
// * objects.
// *
// * If we have no children, we can start allocating from the beginning of
// * our untyped, regardless of what the "free" value in the cap states.
// * (This may happen if all of the objects beneath us got deleted).
// *
// * If we have children, we just keep allocating from the "free" value
// * recorded in the cap.
// */
// -- https://github.com/seL4/seL4/blob/master/src/object/untyped.c#L175
// /*
// * Determine the maximum number of objects we can create, and return an
// * error if we don't have enough space.
// *
// * We don't need to worry about alignment in this case, because if anything
// * fits, it will also fit aligned up (by packing it on the right hand side
// * of the untyped).
// */
// -- https://github.com/seL4/seL4/blob/master/src/object/untyped.c#L196
UntypedCap [
/// Index of the first unoccupied byte within this Untyped.
/// This index is limited between MIN_UNTYPED_BITS and max bits number in BlockSizePower.
/// To occupy less bits, the free index is shifted right by MIN_UNTYPED_BITS.
///
/// Free index is used only if this untyped has children, which may be occupying only
/// part of its space.
/// This means an Untyped can be retyped multiple times as long as there is
/// free space left in it.
FreeIndexShifted OFFSET(0) NUMBITS(48) [],
/// Device mapped untypeds cannot be touched by the kernel.
IsDevice OFFSET(57) NUMBITS(1) [],
/// Untyped is 2**BlockSizePower bytes in size
BlockSizePower OFFSET(58) NUMBITS(6) [],
Type OFFSET(64) NUMBITS(5) [
value = 2
],
/// Physical address of untyped.
Ptr OFFSET(80) NUMBITS(48) [],
],
EndpointCap [
Badge OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 4
],
CanGrantReply OFFSET(69) NUMBITS(1) [],
CanGrant OFFSET(70) NUMBITS(1) [],
CanReceive OFFSET(71) NUMBITS(1) [],
CanSend OFFSET(72) NUMBITS(1) [],
Ptr OFFSET(80) NUMBITS(48) [],
],
NotificationCap [ // @todo replace with Event
Badge OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 6
],
CanReceive OFFSET(69) NUMBITS(1) [],
CanSend OFFSET(70) NUMBITS(1) [],
Ptr OFFSET(80) NUMBITS(48) [],
],
ReplyCap [
TCBPtr OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 8
],
ReplyCanGrant OFFSET(126) NUMBITS(1) [],
ReplyMaster OFFSET(127) NUMBITS(1) [],
],
CapNodeCap [
Guard OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 10
],
GuardSize OFFSET(69) NUMBITS(6) [],
Radix OFFSET(75) NUMBITS(6) [],
Ptr OFFSET(81) NUMBITS(47) [],
],
ThreadCap [
Type OFFSET(64) NUMBITS(5) [
value = 12
],
TCBPtr OFFSET(80) NUMBITS(48) [],
],
IrqControlCap [
Type OFFSET(64) NUMBITS(5) [
value = 14
]
],
IrqHandlerCap [
Irq OFFSET(52) NUMBITS(12) [],
Type OFFSET(64) NUMBITS(5) [
value = 16
]
],
ZombieCap [
ZombieID OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 18
],
ZombieType OFFSET(121) NUMBITS(7) []
],
DomainCap [
Type OFFSET(64) NUMBITS(5) [
value = 20
]
],
// https://ts.data61.csiro.au/publications/csiro_full_text/Lyons_MAH_18.pdf
// Resume objects, modelled after KeyKOS [Bomberger et al.1992], are a new object type
// that generalise the “reply capabilities” of baseline seL4. These were capabilities
// to virtual objects created by the kernel on-the-fly in seL4s RPC-style call() operation,
// which sends a message to an endpoint and blocks on a reply. The receiver of the message
// (i.e. the server) receives the reply capability in a magic “reply slot” in its
// capability space. The server replies by invoking that capability. Resume objects
// remove the magic by explicitly representing the reply channel (and the SC-donation chain).
// They also provide more efficient support for stateful servers that handle concurrent client
// sessions.
// The introduction of Resume objects requires some changes to the IPC system-call API.
// The client-style call() operation is unchanged, but server-side equivalent, ReplyRecv
// (previously ReplyWait) replies to a previous request and then blocks on the next one.
// It now must provide an explicit Resume capability; on the send phase, that capability
// identifies the client and returns the SC if appropriate, on the receive phase it is
// populated with new values. The new API makes stateful server implementation more efficient.
// In baseline seL4, the server would have to use at least two extra system calls to save the
// reply cap and later move it back into its magic slot, removing the magic also removes
// the need for the extra system calls.
ResumeCap [
Type OFFSET(64) NUMBITS(5) [
value = 22
]
]
}
// mod aarch64 {
// ARM-specific caps
register_bitfields! {
u128,
FrameCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 1
],
Size OFFSET(69) NUMBITS(2) [],
VMRights OFFSET(71) NUMBITS(2) [],
IsDevice OFFSET(73) NUMBITS(1) [],
MappedAddress OFFSET(80) NUMBITS(48) [] // VirtAddr
],
PageTableCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 3
],
IsMapped OFFSET(79) NUMBITS(1) [],
MappedAddress OFFSET(80) NUMBITS(28) [] // VirtAddr
],
PageDirectoryCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 5
],
IsMapped OFFSET(79) NUMBITS(1) [],
MappedAddress OFFSET(80) NUMBITS(19) [] // VirtAddr
],
PageUpperDirectoryCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 7
],
IsMapped OFFSET(79) NUMBITS(1) [],
MappedAddress OFFSET(80) NUMBITS(10) [] // VirtAddr
],
PageGlobalDirectoryCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 9
],
IsMapped OFFSET(79) NUMBITS(1) []
],
AsidControlCap [
Type OFFSET(64) NUMBITS(5) [
value = 11
]
],
AsidPoolCap [
Type OFFSET(64) NUMBITS(5) [
value = 13
],
ASIDBase OFFSET(69) NUMBITS(16) [],
ASIDPool OFFSET(91) NUMBITS(37) []
]
// For HYP mode:
// VCpuCap [
// Type OFFSET(64) NUMBITS(5) [], // 15
// VCPUPtr OFFSET(80) NUMBITS(48) [],
// ],
}
//================
// Kernel objects
//================
//-- Mapping database (MDB) node: size = 16 bytes
//block mdb_node {
//padding 16 -- highest in word[1]
//field_high mdbNext 46 <-- field_high means "will need sign-extension", also value has 2 lower bits just dropped when setting
//field mdbRevocable 1 -- second bit in word[1]
//field mdbFirstBadged 1 -- lowest in word[1]
//
//field mdbPrev 64 -- enter lowest word (word[0]) in sel4
//}
register_bitfields! {
u128,
CapDerivationNode [
FirstBadged OFFSET(0) NUMBITS(1) [
Disable = 0,
Enable = 1
],
Revocable OFFSET(1) NUMBITS(1) [
Disable = 0,
Enable = 1
],
// -- 2 bits still free here --
// Next CTE node address -- per cteInsert this is address of the entire CTE slot
// cap derivation slots are supposedly aligned in u128 boundary (16 bytes) this means we can
// drop bottom 4 bits from it in these fields.
Next OFFSET(4) NUMBITS(44) [], // 16-bytes-aligned, size of canonical phys address is 48 bits
// -- 16 bits still free here --
// -- New word doundary --
// -- 4 bits still free here --
// Prev CTE node address -- per cteInsert this is address of the entire CTE slot
Prev OFFSET(68) NUMBITS(44) []
// -- 16 bits still free here --
]
}
/// Opaque capability object, manipulated by the kernel.
pub trait Capability {
///
/// Is this capability arch-specific?
///
fn is_arch(&self) -> bool;
///
/// Retrieve this capability as scalar value.
///
fn as_u128(&self) -> u128;
}
macro_rules! capdefs {
($($name:ident),*) => {
paste! {
$(
#[doc = "Wrapper representing `" $name "Capability`."]
pub struct [<$name Capability>](LocalRegisterCopy<u128, [<$name Cap>]::Register>);
impl Capability for [<$name Capability>] {
#[inline]
fn as_u128(&self) -> u128 {
self.0.into()
}
#[inline]
fn is_arch(&self) -> bool {
([<$name Cap>]::Type::Value::value as u8) % 2 != 0
}
}
impl TryFrom<u128> for [<$name Capability>] {
type Error = CapError;
fn try_from(v: u128) -> Result<[<$name Capability>], Self::Error> {
let reg = LocalRegisterCopy::<_, [<$name Cap>]::Register>::new(v);
if reg.read([<$name Cap>]::Type) == u128::from([<$name Cap>]::Type::value) {
Ok([<$name Capability>](LocalRegisterCopy::new(v)))
} else {
Err(Self::Error::InvalidCapabilityType)
}
}
}
impl From<[<$name Capability>]> for u128 {
#[inline]
fn from(v: [<$name Capability>]) -> u128 {
v.as_u128()
}
}
)*
}
}
}
// Generic caps - @todo move to src/caps/
capdefs! {
Null, Untyped, Endpoint,
Notification, Reply, CapNode,
Thread, IrqControl, IrqHandler,
Zombie, Domain, Resume
}
// ARM-specific caps
capdefs! {
Frame, PageTable, PageDirectory,
PageUpperDirectory, PageGlobalDirectory,
AsidControl, AsidPool
}
impl NullCapability {
/// Create a Null capability.
///
/// Such capabilities are invalid and can not be used for anything.
pub fn new() -> NullCapability {
NullCapability(LocalRegisterCopy::new(u128::from(NullCap::Type::value)))
}
}
// @todo retyping a device capability requires specifying memory base exactly, can't just pick next frame?
/// Capability to a block of untyped memory.
/// Can be retyped into more usable types.
impl UntypedCapability {
const MIN_BITS: usize = 4;
const MAX_BITS: usize = 47;
/// This untyped belongs to device memory (will not be zeroed on allocation).
pub fn is_device(&self) -> bool {
self.0.read(UntypedCap::IsDevice) == 1
}
/// Return untyped block size in bytes.
pub fn block_size(&self) -> usize {
1 << self.0.read(UntypedCap::BlockSizePower)
}
// FreeIndex OFFSET(0) NUMBITS(48) [],
/// Return free area offset in this block in bytes.
pub fn free_area_offset(&self) -> usize {
use core::convert::TryInto;
Self::free_index_to_offset(
self.0
.read(UntypedCap::FreeIndexShifted)
.try_into()
.unwrap(),
)
}
/// Return start address of this untyped block.
pub fn base(&self) -> PhysAddr {
(self.0.read(UntypedCap::Ptr) as u64).into() // @todo implement TryFrom<u128> for PhysAddr
}
// #define MAX_FREE_INDEX(sizeBits) (BIT( (sizeBits) - seL4_MinUntypedBits ))
/// Calculate maximum free index value based on allowed size bits.
pub fn max_free_index_from_bits(size_bits: usize) -> usize {
assert!(size_bits >= Self::MIN_BITS);
assert!(size_bits <= Self::MAX_BITS);
1 << (size_bits - Self::MIN_BITS)
}
// #define FREE_INDEX_TO_OFFSET(freeIndex) ((freeIndex)<<seL4_MinUntypedBits)
/// Convert free index to byte offset.
fn free_index_to_offset(index: usize) -> usize {
index << Self::MIN_BITS
}
// #define OFFSET_TO_FREE_INDEX(offset) ((offset)>>seL4_MinUntypedBits)
/// Convert byte offset to free index.
/// @todo Check proper offset alignment!
fn offset_to_free_index(offset: usize) -> usize {
offset >> Self::MIN_BITS
}
}
// Endpoints support all 10 IPC variants (see COMP9242 slides by Gernot)
impl EndpointCapability {}
// Notifications support NBSend (Signal), Wait and NBWait (Poll) (see COMP9242 slides by Gernot)
// Other objects support only Call() (see COMP9242 slides by Gernot)
// Appear as (kernel-implemented) servers
// • Each has a kernel-defined protocol
// • operations encoded in message tag
// • parameters passed in message words
// • Mostly hidden behind “syscall” wrappers
// * Capability slots: 16 bytes of memory per slot (exactly one capability). --?
// CapNode describes `a given number of capability slots` with `a given guard`
// of `a given guard size` bits.
// @todo const generic on number of capabilities contained in the node? currently only contains a Cap
// capnode_cap has a pptr, guard_size, guard and radix
// this is enough to address a cap in the capnode contents
// by having a root capnode cap we can traverse the whole tree.
impl CapNodeCapability {
/// Create a capability to CapNode.
///
/// CapNode capabilities allow to address a capability node tree entry.
pub fn new(pptr: u64, radix: u32, guard_size: u32, guard: u64) -> CapNodeCapability {
CapNodeCapability(LocalRegisterCopy::new(u128::from(
CapNodeCap::Type::value
+ CapNodeCap::Radix.val(radix.into())
+ CapNodeCap::GuardSize.val(guard_size.into())
+ CapNodeCap::Guard.val(guard.into())
+ CapNodeCap::Ptr.val(pptr.into()),
)))
}
/// Create new root node.
pub fn new_root(pptr: u64) -> CapNodeCapability {
const CONFIG_ROOT_CAPNODE_SIZE_BITS: u32 = 12;
const WORD_BITS: u32 = 64;
CapNodeCapability::new(
pptr,
CONFIG_ROOT_CAPNODE_SIZE_BITS,
WORD_BITS - CONFIG_ROOT_CAPNODE_SIZE_BITS,
0,
)
}
// pub const fn from_capability(cap: dyn Capability) -> CapNodeCapability {
// let reg = LocalRegisterCopy::<_, CapNodeCap::Register>::new(cap.as_u128());
// //assert_eq!(
// // reg.read(CapNodeCap::Type),
// // u128::from(CapNodeCap::Type::value)
// //);
// CapNodeCapability(reg)
// }
/// @internal
pub fn write_slot(&mut self, slot: usize, cap: &dyn Capability) {
let ptr = self.0.read(CapNodeCap::Ptr);
let size =
(1usize << self.0.read(CapNodeCap::Radix)) * core::mem::size_of::<CapTableEntry>();
let slice = unsafe { core::slice::from_raw_parts_mut(ptr as *mut CapTableEntry, size) };
slice[slot].capability = cap.as_u128();
slice[slot].derivation = DerivationTreeNode::empty()
.set_revocable(true)
.set_first_badged(true);
}
}
/// Wrapper for CapDerivationNode
#[derive(Clone, Debug)]
pub struct DerivationTreeNode(LocalRegisterCopy<u128, CapDerivationNode::Register>);
/// Errors that may happen in capability derivation tree operations.
#[derive(Debug, PartialEq, Snafu)]
pub enum DerivationTreeError {
/// Previous link is invalid.
InvalidPrev,
/// Next link is invalid.
InvalidNext,
}
// In seL4, the MDB is stored as a doubly-linked list, representing the **preorder-DFS** through
// the hierarchy of capabilities. This data structure allows easy insertion of a capability
// given its immediate ancestor or a copy, and easy checking for existence of copies and descendants.
// But when no relations are known beforehand, finding the position to place a new capability
// requires a O(n) linear scan through the list, as does finding ancestors and descendants
// of a capability given just the capabilitys value. This operation is performed in
// the non-preemptable kernel, creating a scheduling hole that is problematic for real-time applications.
// To reduce the complexity of operations described above, we replace the MDBs linked list with
// a more suitable search data structure.
// -- nevill-master-thesis Using Capabilities for OS Resource Management
// sel4: mdb_node_t
impl DerivationTreeNode {
const ADDR_BIT_SHIFT: usize = 4;
fn empty() -> Self {
Self(LocalRegisterCopy::new(0))
}
// Unlike mdb_node_new we do not pass revocable and firstBadged flags here, they are enabled
// using builder interface set_first_badged() and set_revocable().
fn new(prevPtr: PhysAddr, nextPtr: PhysAddr) -> Self {
Self::empty().set_prev(prevPtr).set_next(nextPtr)
}
/// Get previous link in derivation tree.
/// Previous link exists if this is a derived capability.
///
/// SAFETY: it is UB to get prev reference from a null Prev pointer.
pub unsafe fn get_prev(&self) -> CapTableEntry {
let ptr = (self.0.read(CapDerivationNode::Prev) << ADDR_BIT_SHIFT) as *const CapTableEntry;
(*ptr).clone()
}
/// Try to get previous link in derivation tree.
/// Previous link exists if this is a derived capability.
pub fn try_get_prev(&self) -> Result<CapTableEntry, DerivationTreeError> {
if self.0.read(CapDerivationNode::Prev) == 0 {
Err(DerivationTreeError::InvalidPrev)
} else {
Ok(unsafe { self.get_prev() })
}
}
pub fn set_prev(&mut self, prevPtr: PhysAddr) -> Self {
self.0
.write(CapDerivationNode::Prev(prevPtr >> ADDR_BIT_SHIFT));
self
}
/// Get next link in derivation tree.
/// Next link exists if this capability has derived capabilities or siblings.
///
/// SAFETY: it is UB to get next reference from a null Next pointer.
pub unsafe fn get_next(&self) -> CapTableEntry {
let ptr = (self.0.read(CapDerivationNode::Next) << ADDR_BIT_SHIFT) as *const CapTableEntry;
(*ptr).clone()
}
/// Try to get next link in derivation tree.
/// Next link exists if this capability has derived capabilities or siblings.
pub fn try_get_next(&self) -> Result<CapTableEntry, DerivationTreeError> {
if self.0.read(CapDerivationNode::Next) == 0 {
Err(DerivationTreeError::InvalidNext)
} else {
Ok(unsafe { self.get_next() })
}
}
pub fn set_next(&mut self, nextPtr: PhysAddr) -> Self {
self.0
.write(CapDerivationNode::Next(nextPtr >> ADDR_BIT_SHIFT));
self
}
/// Builder interface to modify firstBadged flag
/// @todo Describe the firstBadged flag and what it does.
fn set_first_badged(mut self, enable: bool) -> Self {
self.0.modify(if enable {
CapDerivationNode::FirstBadged::Enable
} else {
CapDerivationNode::FirstBadged::Disable
});
self
}
/// Builder interface to modify revocable flag
/// @todo Describe the revocable flag and what it does.
fn set_revocable(mut self, enable: bool) -> Self {
self.0.modify(if enable {
CapDerivationNode::Revocable::Enable
} else {
CapDerivationNode::Revocable::Disable
});
self
}
}
/// Errors in capability operations.
#[derive(Debug, Snafu)]
pub enum CapError {
/// Unable to create capability, exact reason TBD.
CannotCreate,
/// Capability has a type incompatible with the requested operation.
InvalidCapabilityType,
}
// -- cte_t from seL4
// structures.h:140
// /* Capability table entry (CTE) */
// struct cte {
// cap_t cap; // two words
// mdb_node_t cteMDBNode; // two words
// }; // -- four words: u256, 32 bytes.
// typedef struct cte cte_t;
/// Each entry in capability tree contains capability value and its position in the derivation tree.
#[derive(Clone)]
pub struct CapTableEntry {
capability: u128,
derivation: DerivationTreeNode,
}
impl fmt::Debug for CapTableEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", self.capability) // @todo
}
}
impl Default for CapTableEntry {
fn default() -> Self {
Self::empty()
}
}
impl CapTableEntry {
/// Temporary for testing:
fn empty() -> CapTableEntry {
CapTableEntry {
capability: 0,
derivation: DerivationTreeNode::empty(),
}
}
// We need to pass reference to the parent entry so that we can set up derivation pointers.
// @todo should be &mut since we need to set up Next pointer in parent also.
// @fixme this cannot work well unless we modify already allocated cap table entry in the table.
// (otherwise Next pointer will be invalid)
// sel4: cteInsert()
fn derived_from(&mut self, parent: &mut CapTableEntry) {
self.derivation.set_prev(&parent as *const CapTableEntry);
parent.set_next(&self as *const CapTableEntry);
}
}
/*
struct CapNodePath {
/// Index contains `depth` lowermost bits of the path.
index: u64,
/// Depth specifies the remaining amount of bits left to traverse in the path.
/// Once depth reaches zero, the selected CapNode slot is the final target.
depth: usize,
}
struct CapNodeRootedPath {
root: CapNode,
path: CapNodePath,
}
// sel4: cnode_capdata_t
// @todo just use CapNodeCap
//struct CapNodeConfig { <-- for each CapTable we would need these..
// guard: u64,
// guard_bits: usize,
//}
// @note src and dest are swapped here, compared to seL4 api
impl CapNode {
// Derives a capability into a new, less powerful one, with potentially added Badge.
fn mint(
src: CapNodeRootedPath, // can be just CapNodePath since it's relative (is it?) to this CapNode.
dest: CapNodePath,
rights: CapRights,
badge: Badge,
) -> Result<(), CapError> {
unimplemented!();
}
// [wip] is copy a derivation too? - yes it is - kernel_final.c:15769
fn copy(src: CapNodeRootedPath, dest: CapNodePath, rights: CapRights) -> Result<(), CapError> {
unimplemented!();
}
fn r#move(src: CapNodeRootedPath, dest: CapNodePath) -> Result<(), CapError> {
unimplemented!();
}
fn mutate(src: CapNodeRootedPath, dest: CapNodePath, badge: Badge) -> Result<(), CapError> {
unimplemented!();
}
fn rotate(
src: CapNodeRootedPath,
dest: CapNodePath,
dest_badge: Badge,
pivot: CapNodeRootedPath,
pivot_badge: Badge,
) -> Result<(), CapError> {
unimplemented!();
}
fn delete(path: CapNodePath) -> Result<(), CapError> {
unimplemented!();
}
fn revoke(path: CapNodePath) -> Result<(), CapError> {
unimplemented!();
}
fn save_caller(r#where: CapNodePath) -> Result<(), CapError> { // save_reply_cap() in sel4
unimplemented!();
}
fn cancel_badged_sends(path: CapNodePath) -> Result<(), CapError> {
unimplemented!();
}
}*/
/// Structure holding a number of capabilities.
// In seL4 the capnode is capability to an object called CapTable btw:
// case seL4_CapTableObject:
// return cap_cnode_cap_new(userSize, 0, 0, CTE_REF(regionBase));
struct CapTable<const SIZE_BITS: usize>
where
[CapTableEntry; 1 << SIZE_BITS]: Sized,
{
items: [CapTableEntry; 1 << SIZE_BITS],
}
/// Conceptually a threads CapSpace is the portion of the directed graph that is reachable
/// starting with the CapNode capability that is its CapSpace root.
struct CapSpace {
// cap_space_root: CapNodePath, -- probably not a path but direct CapNode pointer??
}
//impl CapNode for CapSpace {} -- ?
impl ThreadCapability {}
impl IrqControlCapability {}
impl IrqHandlerCapability {}
#[cfg(test)]
mod tests {
use super::*;
#[test_case]
fn create_empty_cap_table() {
let table = CapTable::<5> {
items: Default::default(),
};
assert_eq!(table.items[0].capability, NullCapability::new().into());
assert_eq!(table.items[31].capability, NullCapability::new().into());
// Doesn't even compile:
// assert_eq!(table.items[32].capability, NullCapability::new().into());
}
#[test_case]
fn first_capability_derivation_has_no_prev_link() {
let entry = CapTableEntry::empty();
assert!(entry
.derivation
.try_get_prev()
.contains_err(&DerivationTreeError::InvalidPrev));
}
// Impl strategy
// 1. Make capabilities list
// 2. Fill it with capabilities
// 3. Test capability manipulation functions - mint/clone/revoke
// 4. Validate capability path, capability contents and capability derivation chain at each step
// 5. Start with Untyped capabilities and implement Retype()
// typedef enum api_object { -- basic list of API types of objects:
// seL4_UntypedObject,
// seL4_TCBObject,
// seL4_EndpointObject,
// seL4_NotificationObject,
// seL4_CapTableObject,
// 6. Retype to TCB and implement Thread capability to run threads (in priv mode first?)
}
// @todo Use bitmatch over cap Type field?
// Could be interesting if usable. See https://github.com/porglezomp/bitmatch
// Maybe look at https://lib.rs/crates/enumflags2 too

View File

@ -0,0 +1,32 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
crate::{
capdef,
caps::{CapError, Capability},
},
core::convert::TryFrom,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
AsidControlCap [
Type OFFSET(64) NUMBITS(5) [
value = 11
]
]
}
capdef! { AsidControl }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,34 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
crate::{
capdef,
caps::{CapError, Capability},
},
core::convert::TryFrom,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
AsidPoolCap [
Type OFFSET(64) NUMBITS(5) [
value = 13
],
ASIDBase OFFSET(69) NUMBITS(16) [],
ASIDPool OFFSET(91) NUMBITS(37) []
]
}
capdef! { AsidPool }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,38 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
crate::{
capdef,
caps::{CapError, Capability},
},
core::convert::TryFrom,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
FrameCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 1
],
Size OFFSET(69) NUMBITS(2) [],
VMRights OFFSET(71) NUMBITS(2) [],
IsDevice OFFSET(73) NUMBITS(1) [],
MappedAddress OFFSET(80) NUMBITS(48) [] // VirtAddr
]
}
capdef! { Frame }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,9 @@
//! AArch64-specific capabilities.
mod asid_control_cap;
mod asid_pool_cap;
mod frame;
mod page_directory_cap;
mod page_global_directory_cap;
mod page_table_cap;
mod page_upper_directory_cap;

View File

@ -0,0 +1,36 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
crate::{
capdef,
caps::{CapError, Capability},
},
core::convert::TryFrom,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
PageDirectoryCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 5
],
IsMapped OFFSET(79) NUMBITS(1) [],
MappedAddress OFFSET(80) NUMBITS(19) [] // VirtAddr
]
}
capdef! { PageDirectory }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,35 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
crate::{
capdef,
caps::{CapError, Capability},
},
core::convert::TryFrom,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
PageGlobalDirectoryCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 9
],
IsMapped OFFSET(79) NUMBITS(1) []
]
}
capdef! { PageGlobalDirectory }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,36 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
crate::{
capdef,
caps::{CapError, Capability},
},
core::convert::TryFrom,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
PageTableCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 3
],
IsMapped OFFSET(79) NUMBITS(1) [],
MappedAddress OFFSET(80) NUMBITS(28) [] // VirtAddr
],
}
capdef! { PageTable }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,36 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
crate::{
capdef,
caps::{CapError, Capability},
},
core::convert::TryFrom,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
PageUpperDirectoryCap [
MappedASID OFFSET(0) NUMBITS(16) [],
BasePtr OFFSET(16) NUMBITS(48) [], // PhysAddr
Type OFFSET(64) NUMBITS(5) [
value = 7
],
IsMapped OFFSET(79) NUMBITS(1) [],
MappedAddress OFFSET(80) NUMBITS(10) [] // VirtAddr
]
}
capdef! { PageUpperDirectory }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,84 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{
captable::CapTableEntry, derivation_tree::DerivationTreeNode, CapError, Capability, TryFrom,
},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
CapNodeCap [
Guard OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 10
],
GuardSize OFFSET(69) NUMBITS(6) [],
Radix OFFSET(75) NUMBITS(6) [],
Ptr OFFSET(81) NUMBITS(47) [],
]
}
capdef! { CapNode }
//=====================
// Cap implementation
//=====================
impl CapNodeCapability {
/// Create a capability to CapNode.
///
/// CapNode capabilities allow to address a capability node tree entry.
pub fn new(pptr: u64, radix: u32, guard_size: u32, guard: u64) -> CapNodeCapability {
CapNodeCapability(LocalRegisterCopy::new(u128::from(
CapNodeCap::Type::value
+ CapNodeCap::Radix.val(radix.into())
+ CapNodeCap::GuardSize.val(guard_size.into())
+ CapNodeCap::Guard.val(guard.into())
+ CapNodeCap::Ptr.val(pptr.into()),
)))
}
/// Create new root node.
pub fn new_root(pptr: u64) -> CapNodeCapability {
const CONFIG_ROOT_CAPNODE_SIZE_BITS: u32 = 12;
const WORD_BITS: u32 = 64;
CapNodeCapability::new(
pptr,
CONFIG_ROOT_CAPNODE_SIZE_BITS,
WORD_BITS - CONFIG_ROOT_CAPNODE_SIZE_BITS,
0,
)
}
// pub const fn from_capability(cap: dyn Capability) -> CapNodeCapability {
// let reg = LocalRegisterCopy::<_, CapNodeCap::Register>::new(cap.as_u128());
// //assert_eq!(
// // reg.read(CapNodeCap::Type),
// // u128::from(CapNodeCap::Type::value)
// //);
// CapNodeCapability(reg)
// }
/// @internal
pub fn write_slot(&mut self, slot: usize, cap: &dyn Capability) {
let ptr = self.0.read(CapNodeCap::Ptr);
let size =
(1usize << self.0.read(CapNodeCap::Radix)) * core::mem::size_of::<CapTableEntry>();
let slice = unsafe { core::slice::from_raw_parts_mut(ptr as *mut CapTableEntry, size) };
slice[slot].capability = cap.as_u128();
slice[slot].derivation = DerivationTreeNode::empty()
.set_revocable(true)
.set_first_badged(true);
}
}

View File

@ -0,0 +1,183 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {super::derivation_tree::DerivationTreeNode, crate::memory::PhysAddr, core::fmt};
// * Capability slots: 16 bytes of memory per slot (exactly one capability). --?
// CapNode describes `a given number of capability slots` with `a given guard`
// of `a given guard size` bits.
// @todo const generic on number of capabilities contained in the node? currently only contains a Cap
// capnode_cap has a pptr, guard_size, guard and radix
// this is enough to address a cap in the capnode contents
// by having a root capnode cap we can traverse the whole tree.
// -- cte_t from seL4
// structures.h:140
// /* Capability table entry (CTE) */
// struct cte {
// cap_t cap; // two words
// mdb_node_t cteMDBNode; // two words
// }; // -- four words: u256, 32 bytes.
// typedef struct cte cte_t;
/// Each entry in capability tree contains capability value and its position in the derivation tree.
#[derive(Clone)]
pub(crate) struct CapTableEntry {
pub(crate) capability: u128,
pub(crate) derivation: DerivationTreeNode,
}
impl fmt::Debug for CapTableEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", self.capability) // @todo
}
}
impl Default for CapTableEntry {
fn default() -> Self {
Self::empty()
}
}
impl CapTableEntry {
/// Temporary for testing:
fn empty() -> CapTableEntry {
CapTableEntry {
capability: 0,
derivation: DerivationTreeNode::empty(),
}
}
// We need to pass reference to the parent entry so that we can set up derivation pointers.
// @todo should be &mut since we need to set up Next pointer in parent also.
// @fixme this cannot work well unless we modify already allocated cap table entry in the table.
// (otherwise Next pointer will be invalid)
// sel4: cteInsert()
fn derived_from(&mut self, parent: &mut CapTableEntry) {
self.derivation
.set_prev(&parent as *const CapTableEntry as PhysAddr);
parent.set_next(&self as *const CapTableEntry);
}
}
/*
struct CapNodePath {
/// Index contains `depth` lowermost bits of the path.
index: u64,
/// Depth specifies the remaining amount of bits left to traverse in the path.
/// Once depth reaches zero, the selected CapNode slot is the final target.
depth: usize,
}
struct CapNodeRootedPath {
root: CapNode,
path: CapNodePath,
}
// sel4: cnode_capdata_t
// @todo just use CapNodeCap
//struct CapNodeConfig { <-- for each CapTable we would need these..
// guard: u64,
// guard_bits: usize,
//}
// @note src and dest are swapped here, compared to seL4 api
impl CapNode {
// Derives a capability into a new, less powerful one, with potentially added Badge.
fn mint(
src: CapNodeRootedPath, // can be just CapNodePath since it's relative (is it?) to this CapNode.
dest: CapNodePath,
rights: CapRights,
badge: Badge,
) -> Result<(), CapError> {
unimplemented!();
}
// [wip] is copy a derivation too? - yes it is - kernel_final.c:15769
fn copy(src: CapNodeRootedPath, dest: CapNodePath, rights: CapRights) -> Result<(), CapError> {
unimplemented!();
}
fn r#move(src: CapNodeRootedPath, dest: CapNodePath) -> Result<(), CapError> {
unimplemented!();
}
fn mutate(src: CapNodeRootedPath, dest: CapNodePath, badge: Badge) -> Result<(), CapError> {
unimplemented!();
}
fn rotate(
src: CapNodeRootedPath,
dest: CapNodePath,
dest_badge: Badge,
pivot: CapNodeRootedPath,
pivot_badge: Badge,
) -> Result<(), CapError> {
unimplemented!();
}
fn delete(path: CapNodePath) -> Result<(), CapError> {
unimplemented!();
}
fn revoke(path: CapNodePath) -> Result<(), CapError> {
unimplemented!();
}
fn save_caller(r#where: CapNodePath) -> Result<(), CapError> { // save_reply_cap() in sel4
unimplemented!();
}
fn cancel_badged_sends(path: CapNodePath) -> Result<(), CapError> {
unimplemented!();
}
}*/
/// Structure holding a number of capabilities.
// In seL4 the capnode is capability to an object called CapTable btw:
// case seL4_CapTableObject:
// return cap_cnode_cap_new(userSize, 0, 0, CTE_REF(regionBase));
struct CapTable<const SIZE_BITS: usize>
where
[CapTableEntry; 1 << SIZE_BITS]: Sized,
{
items: [CapTableEntry; 1 << SIZE_BITS],
}
/// Conceptually a threads CapSpace is the portion of the directed graph that is reachable
/// starting with the CapNode capability that is its CapSpace root.
struct CapSpace {
// cap_space_root: CapNodePath, -- probably not a path but direct CapNode pointer??
}
//impl CapNode for CapSpace {} -- ?
#[cfg(test)]
mod tests {
use super::*;
#[test_case]
fn create_empty_cap_table() {
let table = CapTable::<5> {
items: Default::default(),
};
assert_eq!(table.items[0].capability, NullCapability::new().into());
assert_eq!(table.items[31].capability, NullCapability::new().into());
// Doesn't even compile:
// assert_eq!(table.items[32].capability, NullCapability::new().into());
}
#[test_case]
fn first_capability_derivation_has_no_prev_link() {
let entry = CapTableEntry::empty();
assert!(entry
.derivation
.try_get_prev()
.contains_err(&DerivationTreeError::InvalidPrev));
}
// Impl strategy
// 1. Make capabilities list
// 2. Fill it with capabilities
// 3. Test capability manipulation functions - mint/clone/revoke
// 4. Validate capability path, capability contents and capability derivation chain at each step
// 5. Start with Untyped capabilities and implement Retype()
// typedef enum api_object { -- basic list of API types of objects:
// seL4_UntypedObject,
// seL4_TCBObject,
// seL4_EndpointObject,
// seL4_NotificationObject,
// seL4_CapTableObject,
// 6. Retype to TCB and implement Thread capability to run threads (in priv mode first?)
}

View File

@ -0,0 +1,161 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
//! DerivationTree nodes record the tree of inheritance for caps:
//! See the picture on derivation from seL4 manual for how this works: each cap contains a ref to
//! DerivationTree node, which records the previous cap and the following cap(s).
use {
super::captable::CapTableEntry,
crate::memory::PhysAddr,
register::{register_bitfields, LocalRegisterCopy},
snafu::Snafu,
};
//-- Mapping database (MDB) node: size = 16 bytes
//block mdb_node {
//padding 16 -- highest in word[1]
//field_high mdbNext 46 <-- field_high means "will need sign-extension", also value has 2 lower bits just dropped when setting
//field mdbRevocable 1 -- second bit in word[1]
//field mdbFirstBadged 1 -- lowest in word[1]
//
//field mdbPrev 64 -- enter lowest word (word[0]) in sel4
//}
register_bitfields! {
u128,
CapDerivationNode [
FirstBadged OFFSET(0) NUMBITS(1) [
Disable = 0,
Enable = 1
],
Revocable OFFSET(1) NUMBITS(1) [
Disable = 0,
Enable = 1
],
// -- 2 bits still free here --
// Next CTE node address -- per cteInsert this is address of the entire CTE slot
// cap derivation slots are supposedly aligned in u128 boundary (16 bytes) this means we can
// drop bottom 4 bits from it in these fields.
Next OFFSET(4) NUMBITS(44) [], // 16-bytes-aligned, size of canonical phys address is 48 bits
// -- 16 bits still free here --
// -- New word doundary --
// -- 4 bits still free here --
// Prev CTE node address -- per cteInsert this is address of the entire CTE slot
Prev OFFSET(68) NUMBITS(44) []
// -- 16 bits still free here --
]
}
/// Wrapper for CapDerivationNode
#[derive(Clone, Debug)]
pub struct DerivationTreeNode(LocalRegisterCopy<u128, CapDerivationNode::Register>);
/// Errors that may happen in capability derivation tree operations.
#[derive(Debug, PartialEq, Snafu)]
pub enum DerivationTreeError {
/// Previous link is invalid.
InvalidPrev,
/// Next link is invalid.
InvalidNext,
}
// In seL4, the MDB is stored as a doubly-linked list, representing the **preorder-DFS** through
// the hierarchy of capabilities. This data structure allows easy insertion of a capability
// given its immediate ancestor or a copy, and easy checking for existence of copies and descendants.
// But when no relations are known beforehand, finding the position to place a new capability
// requires a O(n) linear scan through the list, as does finding ancestors and descendants
// of a capability given just the capabilitys value. This operation is performed in
// the non-preemptable kernel, creating a scheduling hole that is problematic for real-time applications.
// To reduce the complexity of operations described above, we replace the MDBs linked list with
// a more suitable search data structure.
// -- nevill-master-thesis Using Capabilities for OS Resource Management
// sel4: mdb_node_t
impl DerivationTreeNode {
const ADDR_BIT_SHIFT: usize = 4;
pub(crate) fn empty() -> Self {
Self(LocalRegisterCopy::new(0))
}
// Unlike mdb_node_new we do not pass revocable and firstBadged flags here, they are enabled
// using builder interface set_first_badged() and set_revocable().
pub(crate) fn new(prev_ptr: PhysAddr, next_ptr: PhysAddr) -> Self {
Self::empty().set_prev(prev_ptr).set_next(next_ptr)
}
/// Get previous link in derivation tree.
/// Previous link exists if this is a derived capability.
///
/// SAFETY: it is UB to get prev reference from a null Prev pointer.
pub(crate) unsafe fn get_prev(&self) -> CapTableEntry {
let ptr =
(self.0.read(CapDerivationNode::Prev) << Self::ADDR_BIT_SHIFT) as *const CapTableEntry;
(*ptr).clone()
}
/// Try to get previous link in derivation tree.
/// Previous link exists if this is a derived capability.
pub(crate) fn try_get_prev(&self) -> Result<CapTableEntry, DerivationTreeError> {
if self.0.read(CapDerivationNode::Prev) == 0 {
Err(DerivationTreeError::InvalidPrev)
} else {
Ok(unsafe { self.get_prev() })
}
}
pub(crate) fn set_prev(&mut self, prev_ptr: PhysAddr) -> Self {
self.0
.write(CapDerivationNode::Prev.val((prev_ptr >> Self::ADDR_BIT_SHIFT).into()));
*self
}
/// Get next link in derivation tree.
/// Next link exists if this capability has derived capabilities or siblings.
///
/// SAFETY: it is UB to get next reference from a null Next pointer.
pub(crate) unsafe fn get_next(&self) -> CapTableEntry {
let ptr =
(self.0.read(CapDerivationNode::Next) << Self::ADDR_BIT_SHIFT) as *const CapTableEntry;
(*ptr).clone()
}
/// Try to get next link in derivation tree.
/// Next link exists if this capability has derived capabilities or siblings.
pub(crate) fn try_get_next(&self) -> Result<CapTableEntry, DerivationTreeError> {
if self.0.read(CapDerivationNode::Next) == 0 {
Err(DerivationTreeError::InvalidNext)
} else {
Ok(unsafe { self.get_next() })
}
}
pub(crate) fn set_next(&mut self, next_ptr: PhysAddr) -> Self {
self.0
.write(CapDerivationNode::Next.val((next_ptr >> Self::ADDR_BIT_SHIFT).into()));
*self
}
/// Builder interface to modify firstBadged flag
/// @todo Describe the firstBadged flag and what it does.
pub(crate) fn set_first_badged(mut self, enable: bool) -> Self {
self.0.modify(if enable {
CapDerivationNode::FirstBadged::Enable
} else {
CapDerivationNode::FirstBadged::Disable
});
self
}
/// Builder interface to modify revocable flag
/// @todo Describe the revocable flag and what it does.
pub(crate) fn set_revocable(mut self, enable: bool) -> Self {
self.0.modify(if enable {
CapDerivationNode::Revocable::Enable
} else {
CapDerivationNode::Revocable::Disable
});
self
}
}

View File

@ -0,0 +1,29 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
DomainCap [
Type OFFSET(64) NUMBITS(5) [
value = 20
]
],
}
capdef! { Domain }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,38 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
EndpointCap [
Badge OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 4
],
CanGrantReply OFFSET(69) NUMBITS(1) [],
CanGrant OFFSET(70) NUMBITS(1) [],
CanReceive OFFSET(71) NUMBITS(1) [],
CanSend OFFSET(72) NUMBITS(1) [],
Ptr OFFSET(80) NUMBITS(48) [],
]
}
capdef! { Endpoint }
//=====================
// Cap implementation
//=====================
// Endpoints support all 10 IPC variants (see COMP9242 slides by Gernot)
impl EndpointCapability {}

View File

@ -0,0 +1,31 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
IrqControlCap [
Type OFFSET(64) NUMBITS(5) [
value = 14
]
]
}
capdef! { IrqControl }
//=====================
// Cap implementation
//=====================
impl IrqControlCapability {}

View File

@ -0,0 +1,32 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
IrqHandlerCap [
Irq OFFSET(52) NUMBITS(12) [],
Type OFFSET(64) NUMBITS(5) [
value = 16
]
]
}
capdef! { IrqHandler }
//=====================
// Cap implementation
//=====================
impl IrqHandlerCapability {}

101
nucleus/src/caps/mod.rs Normal file
View File

@ -0,0 +1,101 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
//! Implementation of system capabilities.
// ☐ Rust implementation of capabilities - ?
// ☐ Need to implement in kernel entries storage and lookup
// ☐ cte = cap table entry (a cap_t plus mdb_node_t)
// ☐ mdb = ? (mdb_node_new)
// ☐ sameObjectAs()
// cap_get_capType();//generated
// lookupCapAndSlot();
// cap_domain_cap_new() etc //generated
// create_mapped_it_frame_cap(); //vspace.c
// pptr_of_cap(); -- extracts cap.pptr from cnode_cap
// deriveCap();
// @todo Use bitmatch over cap Type field?
// Could be interesting if usable. See https://github.com/porglezomp/bitmatch
// Maybe look at https://lib.rs/crates/enumflags2 too
use {crate::memory::PhysAddr, core::convert::TryFrom, snafu::Snafu};
mod capnode_cap;
mod captable;
mod derivation_tree;
mod domain_cap;
mod endpoint_cap;
mod irq_control_cap;
mod irq_handler_cap;
mod notification_cap;
mod null_cap;
mod reply_cap;
mod resume_cap;
mod thread_cap;
mod untyped_cap;
mod zombie_cap;
/// Opaque capability object, manipulated by the kernel.
pub trait Capability {
///
/// Is this capability arch-specific?
///
fn is_arch(&self) -> bool;
///
/// Retrieve this capability as scalar value.
///
fn as_u128(&self) -> u128;
}
/// Errors in capability operations.
#[derive(Debug, Snafu)]
pub enum CapError {
/// Unable to create capability, exact reason TBD.
CannotCreate,
/// Capability has a type incompatible with the requested operation.
InvalidCapabilityType,
}
/// Implement default fns and traits for the capability.
#[macro_export]
macro_rules! capdef {
($name:ident) => {
paste! {
#[doc = "Wrapper representing `" $name "Capability`."]
pub struct [<$name Capability>](LocalRegisterCopy<u128, [<$name Cap>]::Register>);
impl Capability for [<$name Capability>] {
#[inline]
fn as_u128(&self) -> u128 {
self.0.into()
}
#[inline]
fn is_arch(&self) -> bool {
([<$name Cap>]::Type::Value::value as u8) % 2 != 0
}
}
impl TryFrom<u128> for [<$name Capability>] {
type Error = CapError;
fn try_from(v: u128) -> Result<[<$name Capability>], Self::Error> {
let reg = LocalRegisterCopy::<_, [<$name Cap>]::Register>::new(v);
if reg.read([<$name Cap>]::Type) == u128::from([<$name Cap>]::Type::value) {
Ok([<$name Capability>](LocalRegisterCopy::new(v)))
} else {
Err(Self::Error::InvalidCapabilityType)
}
}
}
impl From<[<$name Capability>]> for u128 {
#[inline]
fn from(v: [<$name Capability>]) -> u128 {
v.as_u128()
}
}
}
};
}

View File

@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
//! @todo replace with Event
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
NotificationCap [
Badge OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 6
],
CanReceive OFFSET(69) NUMBITS(1) [],
CanSend OFFSET(70) NUMBITS(1) [],
Ptr OFFSET(80) NUMBITS(48) [],
]
}
capdef! { Notification }
//=====================
// Cap implementation
//=====================
// Notifications support NBSend (Signal), Wait and NBWait (Poll) (see COMP9242 slides by Gernot)
// Other objects support only Call() (see COMP9242 slides by Gernot)
// Appear as (kernel-implemented) servers
// • Each has a kernel-defined protocol
// • operations encoded in message tag
// • parameters passed in message words
// • Mostly hidden behind “syscall” wrappers

View File

@ -0,0 +1,38 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
NullCap [
Type OFFSET(64) NUMBITS(5) [
value = 0
]
]
}
capdef! { Null }
//=====================
// Cap implementation
//=====================
impl NullCapability {
/// Create a Null capability.
///
/// Such capabilities are invalid and can not be used for anything.
pub fn new() -> NullCapability {
NullCapability(LocalRegisterCopy::new(u128::from(NullCap::Type::value)))
}
}

View File

@ -0,0 +1,32 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
ReplyCap [
TCBPtr OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 8
],
ReplyCanGrant OFFSET(126) NUMBITS(1) [],
ReplyMaster OFFSET(127) NUMBITS(1) [],
]
}
capdef! { Reply }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,49 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
// https://ts.data61.csiro.au/publications/csiro_full_text/Lyons_MAH_18.pdf
// Resume objects, modelled after KeyKOS [Bomberger et al.1992], are a new object type
// that generalise the “reply capabilities” of baseline seL4. These were capabilities
// to virtual objects created by the kernel on-the-fly in seL4s RPC-style call() operation,
// which sends a message to an endpoint and blocks on a reply. The receiver of the message
// (i.e. the server) receives the reply capability in a magic “reply slot” in its
// capability space. The server replies by invoking that capability. Resume objects
// remove the magic by explicitly representing the reply channel (and the SC-donation chain).
// They also provide more efficient support for stateful servers that handle concurrent client
// sessions.
// The introduction of Resume objects requires some changes to the IPC system-call API.
// The client-style call() operation is unchanged, but server-side equivalent, ReplyRecv
// (previously ReplyWait) replies to a previous request and then blocks on the next one.
// It now must provide an explicit Resume capability; on the send phase, that capability
// identifies the client and returns the SC if appropriate, on the receive phase it is
// populated with new values. The new API makes stateful server implementation more efficient.
// In baseline seL4, the server would have to use at least two extra system calls to save the
// reply cap and later move it back into its magic slot, removing the magic also removes
// the need for the extra system calls.
ResumeCap [
Type OFFSET(64) NUMBITS(5) [
value = 22
]
]
}
capdef! { Resume }
//=====================
// Cap implementation
//=====================

View File

@ -0,0 +1,32 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
ThreadCap [
Type OFFSET(64) NUMBITS(5) [
value = 12
],
TCBPtr OFFSET(80) NUMBITS(48) [],
]
}
capdef! { Thread }
//=====================
// Cap implementation
//=====================
impl ThreadCapability {}

View File

@ -0,0 +1,156 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, PhysAddr, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
// The combination of freeIndex and blockSize must match up with the
// definitions of MIN_SIZE_BITS and MAX_SIZE_BITS
// -- https://github.com/seL4/seL4/blob/master/include/object/structures_32.bf#L18
//
// /* It is assumed that every untyped is within seL4_MinUntypedBits and seL4_MaxUntypedBits
// * (inclusive). This means that every untyped stored as seL4_MinUntypedBits
// * subtracted from its size before it is stored in capBlockSize, and
// * capFreeIndex counts in chunks of size 2^seL4_MinUntypedBits. The seL4_MaxUntypedBits
// * is the minimal untyped that can be stored when considering both how
// * many bits of capBlockSize there are, and the largest offset that can
// * be stored in capFreeIndex */
// +#define MAX_FREE_INDEX(sizeBits) (BIT( (sizeBits) - seL4_MinUntypedBits ))
// +#define FREE_INDEX_TO_OFFSET(freeIndex) ((freeIndex)<<seL4_MinUntypedBits)
// #define GET_FREE_REF(base,freeIndex) ((word_t)(((word_t)(base)) + FREE_INDEX_TO_OFFSET(freeIndex)))
// #define GET_FREE_INDEX(base,free) (((word_t)(free) - (word_t)(base))>>seL4_MinUntypedBits)
// #define GET_OFFSET_FREE_PTR(base, offset) ((void *)(((word_t)(base)) + (offset)))
// +#define OFFSET_TO_FREE_INDEX(offset) ((offset)>>seL4_MinUntypedBits)
//
// exception_t decodeUntypedInvocation(word_t invLabel, word_t length,
// cte_t *slot, cap_t cap,
// extra_caps_t excaps, bool_t call,
// word_t *buffer);
// exception_t invokeUntyped_Retype(cte_t *srcSlot, bool_t reset,
// void *retypeBase, object_t newType,
// word_t userSize, slot_range_t destSlots,
// bool_t deviceMemory);
// // -- https://github.com/seL4/seL4/blob/master/src/object/untyped.c#L276
// -- https://github.com/seL4/seL4/blob/master/include/object/untyped.h
//
// /* Untyped size limits */
// #define seL4_MinUntypedBits 4
// #define seL4_MaxUntypedBits 47
// -- https://github.com/seL4/seL4/blob/master/libsel4/sel4_arch_include/aarch64/sel4/sel4_arch/constants.h#L234
//
// /*
// * Determine where in the Untyped region we should start allocating new
// * objects.
// *
// * If we have no children, we can start allocating from the beginning of
// * our untyped, regardless of what the "free" value in the cap states.
// * (This may happen if all of the objects beneath us got deleted).
// *
// * If we have children, we just keep allocating from the "free" value
// * recorded in the cap.
// */
// -- https://github.com/seL4/seL4/blob/master/src/object/untyped.c#L175
// /*
// * Determine the maximum number of objects we can create, and return an
// * error if we don't have enough space.
// *
// * We don't need to worry about alignment in this case, because if anything
// * fits, it will also fit aligned up (by packing it on the right hand side
// * of the untyped).
// */
// -- https://github.com/seL4/seL4/blob/master/src/object/untyped.c#L196
UntypedCap [
/// Index of the first unoccupied byte within this Untyped.
/// This index is limited between MIN_UNTYPED_BITS and max bits number in BlockSizePower.
/// To occupy less bits, the free index is shifted right by MIN_UNTYPED_BITS.
///
/// Free index is used only if this untyped has children, which may be occupying only
/// part of its space.
/// This means an Untyped can be retyped multiple times as long as there is
/// free space left in it.
FreeIndexShifted OFFSET(0) NUMBITS(48) [],
/// Device mapped untypeds cannot be touched by the kernel.
IsDevice OFFSET(57) NUMBITS(1) [],
/// Untyped is 2**BlockSizePower bytes in size
BlockSizePower OFFSET(58) NUMBITS(6) [],
Type OFFSET(64) NUMBITS(5) [
value = 2
],
/// Physical address of untyped.
Ptr OFFSET(80) NUMBITS(48) [],
]
}
capdef! { Untyped }
//=====================
// Cap implementation
//=====================
// @todo retyping a device capability requires specifying memory base exactly, can't just pick next frame?
/// Capability to a block of untyped memory.
/// Can be retyped into more usable types.
impl UntypedCapability {
const MIN_BITS: usize = 4;
const MAX_BITS: usize = 47;
/// This untyped belongs to device memory (will not be zeroed on allocation).
pub fn is_device(&self) -> bool {
self.0.read(UntypedCap::IsDevice) == 1
}
/// Return untyped block size in bytes.
pub fn block_size(&self) -> usize {
1 << self.0.read(UntypedCap::BlockSizePower)
}
// FreeIndex OFFSET(0) NUMBITS(48) [],
/// Return free area offset in this block in bytes.
pub fn free_area_offset(&self) -> usize {
use core::convert::TryInto;
Self::free_index_to_offset(
self.0
.read(UntypedCap::FreeIndexShifted)
.try_into()
.unwrap(),
)
}
/// Return start address of this untyped block.
pub fn base(&self) -> PhysAddr {
(self.0.read(UntypedCap::Ptr) as u64).into() // @todo implement TryFrom<u128> for PhysAddr
}
// #define MAX_FREE_INDEX(sizeBits) (BIT( (sizeBits) - seL4_MinUntypedBits ))
/// Calculate maximum free index value based on allowed size bits.
pub fn max_free_index_from_bits(size_bits: usize) -> usize {
assert!(size_bits >= Self::MIN_BITS);
assert!(size_bits <= Self::MAX_BITS);
1 << (size_bits - Self::MIN_BITS)
}
// #define FREE_INDEX_TO_OFFSET(freeIndex) ((freeIndex)<<seL4_MinUntypedBits)
/// Convert free index to byte offset.
fn free_index_to_offset(index: usize) -> usize {
index << Self::MIN_BITS
}
// #define OFFSET_TO_FREE_INDEX(offset) ((offset)>>seL4_MinUntypedBits)
/// Convert byte offset to free index.
/// @todo Check proper offset alignment!
fn offset_to_free_index(offset: usize) -> usize {
offset >> Self::MIN_BITS
}
}

View File

@ -0,0 +1,31 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
use {
super::{CapError, Capability, TryFrom},
crate::capdef,
paste::paste,
register::{register_bitfields, LocalRegisterCopy},
};
//=====================
// Cap definition
//=====================
register_bitfields! {
u128,
ZombieCap [
ZombieID OFFSET(0) NUMBITS(64) [],
Type OFFSET(64) NUMBITS(5) [
value = 18
],
ZombieType OFFSET(121) NUMBITS(7) []
]
}
capdef! { Zombie }
//=====================
// Cap implementation
//=====================

View File

@ -36,6 +36,7 @@ use architecture_not_supported_sorry;
#[macro_use]
pub mod arch;
pub use arch::*;
mod caps;
mod devices;
mod macros;
mod mm;