Refactor: move all caps to their appropriate modules and files
This commit is contained in:
parent
57ae94847d
commit
caa1929a0f
|
@ -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 seL4’s 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 capability’s 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 MDB’s 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 thread’s 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
|
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -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;
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 thread’s 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?)
|
||||||
|
}
|
|
@ -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 capability’s 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 MDB’s 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -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 {}
|
|
@ -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 {}
|
|
@ -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 {}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -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
|
|
@ -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)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -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 seL4’s 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
|
||||||
|
//=====================
|
|
@ -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 {}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
//=====================
|
|
@ -36,6 +36,7 @@ use architecture_not_supported_sorry;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
pub use arch::*;
|
pub use arch::*;
|
||||||
|
mod caps;
|
||||||
mod devices;
|
mod devices;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod mm;
|
mod mm;
|
||||||
|
|
Loading…
Reference in New Issue