mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45:19 +08:00
feat: add initial cnode and untyped support
This commit is contained in:
parent
59514440e0
commit
615dac5f5c
@ -1,4 +1,4 @@
|
|||||||
#[derive(Clone, Copy, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
||||||
pub enum ObjectType {
|
pub enum ObjectType {
|
||||||
Null = 0,
|
Null = 0,
|
||||||
CNode = 1,
|
CNode = 1,
|
||||||
@ -18,3 +18,26 @@ impl Default for ObjectType {
|
|||||||
Self::Null
|
Self::Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ObjectType {
|
||||||
|
pub const fn size(&self, user_obj_bits: usize) -> usize {
|
||||||
|
1 << self.bits(user_obj_bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn bits(&self, user_obj_bits: usize) -> usize {
|
||||||
|
#![rustfmt::skip]
|
||||||
|
match self {
|
||||||
|
ObjectType::Null => 0, // TODO: fill it!
|
||||||
|
ObjectType::CNode => 0, // TODO: fill it!
|
||||||
|
ObjectType::TCB => 0, // TODO: fill it!
|
||||||
|
ObjectType::SchedCtx => 0, // TODO: fill it!
|
||||||
|
ObjectType::Endpoint => 0, // TODO: fill it!
|
||||||
|
ObjectType::Reply => 0, // TODO: fill it!
|
||||||
|
ObjectType::Notification => 0, // TODO: fill it!
|
||||||
|
ObjectType::Frame => 0, // TODO: fill it!
|
||||||
|
ObjectType::PageTable => 0, // TODO: fill it!
|
||||||
|
ObjectType::Interrupt => 0, // TODO: fill it!
|
||||||
|
ObjectType::Untyped => user_obj_bits,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
use crate::cap::ObjectType;
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
||||||
pub enum Fault {
|
|
||||||
Ok,
|
|
||||||
CapFault(CapFault),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum CapFault {
|
pub enum CapFault {
|
||||||
// vallina
|
|
||||||
InvalidRoot,
|
InvalidRoot,
|
||||||
MissingCapability {
|
MissingCapability {
|
||||||
bits_left: usize,
|
bits_left: usize,
|
||||||
@ -16,13 +9,19 @@ pub enum CapFault {
|
|||||||
bits_left: usize,
|
bits_left: usize,
|
||||||
},
|
},
|
||||||
GuardMismatch {
|
GuardMismatch {
|
||||||
guard_found: usize,
|
|
||||||
bits_left: usize,
|
bits_left: usize,
|
||||||
|
guard_found: usize,
|
||||||
guard_size: usize,
|
guard_size: usize,
|
||||||
},
|
},
|
||||||
// additional
|
|
||||||
TypeMismatch {
|
|
||||||
expected: ObjectType,
|
|
||||||
found: ObjectType,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
||||||
|
pub enum SysError {
|
||||||
|
Ok,
|
||||||
|
CapTypeMismatch, // InvalidCapability
|
||||||
|
SlotNotEmpty, // DeleteFirst
|
||||||
|
InvalidArgument,
|
||||||
|
RangeError,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SysResult<T> = Result<T, SysError>;
|
@ -1,7 +1,8 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![feature(custom_inner_attributes)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate num_derive;
|
extern crate num_derive;
|
||||||
|
|
||||||
pub mod cap;
|
pub mod cap;
|
||||||
pub mod failure;
|
pub mod error;
|
||||||
|
@ -2,8 +2,9 @@ use api::cap::ObjectType;
|
|||||||
use vspace::addr::PhysAddr;
|
use vspace::addr::PhysAddr;
|
||||||
|
|
||||||
/// RawCap is the specific implementation of capability which stores in CNode
|
/// RawCap is the specific implementation of capability which stores in CNode
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct RawCap {
|
pub struct RawCap {
|
||||||
|
// TODO: this could be an enum, figure out a way to do this
|
||||||
/// args: in vanilla seL4 implementation, a cap use two 64-bit words to store all information,
|
/// args: in vanilla seL4 implementation, a cap use two 64-bit words to store all information,
|
||||||
/// but we'll waste some space rather than using bitfield to simplify the implementation
|
/// but we'll waste some space rather than using bitfield to simplify the implementation
|
||||||
pub args: [usize; 2],
|
pub args: [usize; 2],
|
||||||
|
130
kernel/src/objects/cnode.rs
Normal file
130
kernel/src/objects/cnode.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
use core::cell::Cell;
|
||||||
|
|
||||||
|
use super::{cap::RawCap, Cap, KernelObject};
|
||||||
|
use crate::arch::layout::mmap_phys_to_virt;
|
||||||
|
use api::{cap::ObjectType, error::CapFault};
|
||||||
|
use utils::MASK;
|
||||||
|
use vspace::addr::{AddressOps, PhysAddr};
|
||||||
|
|
||||||
|
/// CNodeObject is a array of Capabilities (`RawCap`)
|
||||||
|
/// The size of the array is stored in CNodeCap
|
||||||
|
pub type CNodeObject = [Cell<RawCap>];
|
||||||
|
|
||||||
|
impl KernelObject for CNodeObject {
|
||||||
|
const OBJ_TYPE: ObjectType = ObjectType::CNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* in vanilla seL4, CNodeCap layout:
|
||||||
|
* > args[0]: | cap_tag | guard_size | radix | cnode_ptr |
|
||||||
|
* > | [63:59] | [58:53] | [52:47] | [46:0] |
|
||||||
|
* > args[1]: guard
|
||||||
|
*
|
||||||
|
* in our implementation, CNodeCap layout:
|
||||||
|
* > args[0]: [guard_size, radix]
|
||||||
|
* > args[1]: guard
|
||||||
|
* > ptr: cnode_ptr
|
||||||
|
* > cap_type: cap_tag
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub type CNodeCap<'a> = Cap<'a, CNodeObject>;
|
||||||
|
|
||||||
|
impl<'a> CNodeCap<'a> {
|
||||||
|
const GUARD_SIZE_BITS: usize = 6;
|
||||||
|
const GUARD_SIZE_MASK: usize = MASK!(Self::GUARD_SIZE_BITS);
|
||||||
|
const GUARD_SIZE_OFFSET: usize = Self::RADIX_BITS + Self::RADIX_OFFSET;
|
||||||
|
const RADIX_BITS: usize = 6;
|
||||||
|
const RADIX_MASK: usize = MASK!(Self::RADIX_BITS);
|
||||||
|
const RADIX_OFFSET: usize = 0;
|
||||||
|
|
||||||
|
pub fn mint(radix: usize, guard_size: usize, guard: usize, ptr: PhysAddr) -> RawCap {
|
||||||
|
let arg0 = ((radix & Self::RADIX_MASK) << Self::RADIX_OFFSET)
|
||||||
|
| ((guard_size & Self::GUARD_SIZE_MASK) << Self::GUARD_SIZE_OFFSET);
|
||||||
|
let arg1 = guard;
|
||||||
|
|
||||||
|
let cap = RawCap::new(arg0, arg1, ptr, ObjectType::CNode);
|
||||||
|
CNodeCap::try_from(&Cell::new(cap))
|
||||||
|
.unwrap()
|
||||||
|
.as_object()
|
||||||
|
.iter()
|
||||||
|
.for_each(|cell| {
|
||||||
|
cell.set(RawCap::default());
|
||||||
|
});
|
||||||
|
|
||||||
|
cap
|
||||||
|
}
|
||||||
|
|
||||||
|
fn radix(&self) -> usize {
|
||||||
|
(self.cap.get().args[0] >> Self::RADIX_OFFSET) & Self::RADIX_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guard_size(&self) -> usize {
|
||||||
|
(self.cap.get().args[0] >> Self::GUARD_SIZE_OFFSET) & Self::GUARD_SIZE_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guard(&self) -> usize {
|
||||||
|
self.cap.get().args[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CNodeObject length
|
||||||
|
fn length(&self) -> usize {
|
||||||
|
// Return the limited size of the CNode, Rust will help us to check the boundary :)
|
||||||
|
// > the kernel then uses the next most-significant `radix` bits of the
|
||||||
|
// > capability address as an `index` into the CNode to which the CNode capability refers
|
||||||
|
1 << self.radix()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_object(&self) -> &CNodeObject {
|
||||||
|
unsafe {
|
||||||
|
let virt = mmap_phys_to_virt(self.cap.get().ptr);
|
||||||
|
core::slice::from_raw_parts(virt.as_const_ptr(), self.length())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_address_bits(&self, cap_ptr: usize, n_bits: usize) -> Result<&Cell<RawCap>, CapFault> {
|
||||||
|
let mut bits_remaining = n_bits;
|
||||||
|
let mut slot = self.cap;
|
||||||
|
|
||||||
|
while let Ok(cnode) = CNodeCap::try_from(slot) {
|
||||||
|
let radix_bits = cnode.radix();
|
||||||
|
let guard_bits = cnode.guard_size();
|
||||||
|
let level_bits = radix_bits + guard_bits;
|
||||||
|
let cap_guard = cnode.guard();
|
||||||
|
|
||||||
|
assert_ne!(level_bits, 0);
|
||||||
|
|
||||||
|
if level_bits > bits_remaining {
|
||||||
|
return Err(CapFault::DepthMismatch {
|
||||||
|
bits_found: level_bits,
|
||||||
|
bits_left: bits_remaining,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let guard = || {
|
||||||
|
return (cap_ptr >> (bits_remaining - guard_bits)) & MASK!(guard_bits);
|
||||||
|
};
|
||||||
|
|
||||||
|
if guard_bits > bits_remaining || guard() != cap_guard {
|
||||||
|
return Err(CapFault::GuardMismatch {
|
||||||
|
bits_left: bits_remaining,
|
||||||
|
guard_found: cap_guard,
|
||||||
|
guard_size: guard_bits,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = (cap_ptr >> (bits_remaining - level_bits)) & MASK!(radix_bits);
|
||||||
|
slot = unsafe { &*(&cnode.as_object()[offset] as *const _) };
|
||||||
|
|
||||||
|
if bits_remaining == level_bits {
|
||||||
|
return Ok(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bits_remaining -= level_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
if CNodeCap::try_from(slot).is_err() {
|
||||||
|
return Err(CapFault::InvalidRoot);
|
||||||
|
}
|
||||||
|
Ok(slot)
|
||||||
|
}
|
||||||
|
}
|
@ -14,12 +14,17 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use api::{cap::ObjectType, failure::CapFault};
|
use api::{
|
||||||
|
cap::ObjectType,
|
||||||
|
error::{SysError, SysResult},
|
||||||
|
};
|
||||||
use cap::RawCap;
|
use cap::RawCap;
|
||||||
use core::{cell::Cell, marker::PhantomData};
|
use core::{cell::Cell, marker::PhantomData};
|
||||||
|
|
||||||
pub mod cap;
|
pub mod cap;
|
||||||
|
pub mod cnode;
|
||||||
pub mod null;
|
pub mod null;
|
||||||
|
pub mod untyped;
|
||||||
|
|
||||||
/// Cap is the high-level wrapper of RawCap, it's a typed reference to RawCap (which is untyped in Rust)
|
/// Cap is the high-level wrapper of RawCap, it's a typed reference to RawCap (which is untyped in Rust)
|
||||||
/// For the typed objects, we should bound it with an empty traits `KernelObject`
|
/// For the typed objects, we should bound it with an empty traits `KernelObject`
|
||||||
@ -30,14 +35,11 @@ pub struct Cap<'a, T: KernelObject + ?Sized> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: KernelObject + ?Sized> TryFrom<&'a Cell<RawCap>> for Cap<'a, T> {
|
impl<'a, T: KernelObject + ?Sized> TryFrom<&'a Cell<RawCap>> for Cap<'a, T> {
|
||||||
type Error = CapFault;
|
type Error = SysError;
|
||||||
|
|
||||||
fn try_from(new: &'a Cell<RawCap>) -> Result<Self, Self::Error> {
|
fn try_from(new: &'a Cell<RawCap>) -> SysResult<Self> {
|
||||||
if new.get().cap_type != T::OBJ_TYPE {
|
if new.get().cap_type != T::OBJ_TYPE {
|
||||||
Err(CapFault::TypeMismatch {
|
Err(SysError::CapTypeMismatch)
|
||||||
expected: T::OBJ_TYPE,
|
|
||||||
found: new.get().cap_type,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
cap: new,
|
cap: new,
|
||||||
|
129
kernel/src/objects/untyped.rs
Normal file
129
kernel/src/objects/untyped.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
use super::cap::RawCap;
|
||||||
|
use super::cnode::{CNodeCap, CNodeObject};
|
||||||
|
use super::null::NullCap;
|
||||||
|
use super::{Cap, KernelObject};
|
||||||
|
use api::cap::ObjectType;
|
||||||
|
use api::error::{SysError, SysResult};
|
||||||
|
use utils::then::Then;
|
||||||
|
use utils::MASK;
|
||||||
|
use vspace::addr::{align_up, PhysAddr};
|
||||||
|
|
||||||
|
/// UntypedObject is used as raw memory (associated with PhysAddr)
|
||||||
|
/// It can be further retyped to other objects (TCB, CNode ...)
|
||||||
|
pub struct UntypedObject {}
|
||||||
|
|
||||||
|
impl KernelObject for UntypedObject {
|
||||||
|
const OBJ_TYPE: ObjectType = ObjectType::Untyped;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* in vanilla seL4, UntypedCap layout:
|
||||||
|
* > args[0]: | cap_tag | unused | cap_ptr |
|
||||||
|
* > | [63:59] | unused | [38:0] |
|
||||||
|
* > args[1]: | free_index | unused | is_device | block_bits |
|
||||||
|
* > | [63:25] | unused | [6:6] | [5:0] |
|
||||||
|
*
|
||||||
|
* in our implementation, UntypedCap layout:
|
||||||
|
* > NOTE: we are not storing bits, but bytes. free_offset is in bytes.
|
||||||
|
* > args[0]: free_offset
|
||||||
|
* > args[1]: [is_device, block_bits]
|
||||||
|
* > ptr: cap_ptr
|
||||||
|
* > cap_type: cap_tag
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub type UntypedCap<'a> = Cap<'a, UntypedObject>;
|
||||||
|
impl UntypedCap<'_> {
|
||||||
|
pub fn mint(free_offset: usize, block_bits: usize, is_device: bool, ptr: PhysAddr) -> RawCap {
|
||||||
|
RawCap::new(
|
||||||
|
free_offset,
|
||||||
|
((is_device as usize) << 6) | (block_bits & MASK!(6)),
|
||||||
|
ptr,
|
||||||
|
ObjectType::Untyped,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free_offset(&self) -> usize {
|
||||||
|
self.cap.get().args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_free_offset(&self, free_offset: usize) {
|
||||||
|
self.cap.get().args[0] = free_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_device(&self) -> bool {
|
||||||
|
(self.cap.get().args[1] >> 6) & 1 == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_bits(&self) -> usize {
|
||||||
|
self.cap.get().args[1] & MASK!(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_size(&self) -> usize {
|
||||||
|
1 << self.block_bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retype(&self, obj_type: ObjectType, user_obj_bits: usize, slots: &CNodeObject) -> SysResult<()> {
|
||||||
|
/*
|
||||||
|
* vallina seL4: `decode_untiped_invocation`
|
||||||
|
* - _service CPTR to an untyped object.
|
||||||
|
* - type The seL4 object type that we are retyping to.
|
||||||
|
* - size_bits Used to determine the size of variable-sized objects.
|
||||||
|
* - root CPTR to the CNode at the root of the destination CSpace.
|
||||||
|
* - node_index CPTR to the destination CNode. Resolved relative to the root parameter.
|
||||||
|
* - node_depth Number of bits of node_index to translate when addressing the destination CNode.
|
||||||
|
* - node_offset Number of slots into the node at which capabilities start being placed.
|
||||||
|
* - num_objects Number of capabilities to create.
|
||||||
|
*
|
||||||
|
* for our `retype`
|
||||||
|
* - _service is just self
|
||||||
|
* - type is passed by obj_type
|
||||||
|
* - size_bits passed by user_obj_bits
|
||||||
|
* - root + node_index + node_depth are used by `resolve_address_bits`
|
||||||
|
* - cnode[node_offset +: num_objects] being cut-off from previous, passing as slots
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Make sure all slots are empty
|
||||||
|
slots
|
||||||
|
.iter()
|
||||||
|
.any(|cap: &core::cell::Cell<RawCap>| NullCap::try_from(cap).is_err())
|
||||||
|
.then_ok((), SysError::CapTypeMismatch)?;
|
||||||
|
|
||||||
|
// Start allocating from free_offset
|
||||||
|
// Notice: in vallina seL4, it will check whether there are child nodes,
|
||||||
|
// if not, will ignore free_index and start from 0.
|
||||||
|
// We won't do this check, so we need to make sure free_offset is valid anytime.
|
||||||
|
let obj_size = obj_type.size(user_obj_bits);
|
||||||
|
let start_offset = align_up(self.free_offset(), obj_size);
|
||||||
|
|
||||||
|
// Check whether the size is valid
|
||||||
|
let objs_size = obj_size * slots.len();
|
||||||
|
if start_offset + objs_size > self.block_size() {
|
||||||
|
return Err(SysError::RangeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether we are handling device memory
|
||||||
|
if self.is_device() && obj_type != ObjectType::Frame && obj_type != ObjectType::Untyped {
|
||||||
|
// TODO: we might split frame into tera, giga, mega, kilo...
|
||||||
|
return Err(SysError::InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new capabilities in slot
|
||||||
|
for (i, slot) in slots.iter().enumerate() {
|
||||||
|
let addr = self.cap.get().ptr + start_offset + i * obj_size;
|
||||||
|
let new_cap = match obj_type {
|
||||||
|
ObjectType::Untyped => UntypedCap::mint(0, obj_type.bits(user_obj_bits), self.is_device(), addr),
|
||||||
|
ObjectType::CNode => CNodeCap::mint(user_obj_bits, 0, 0, addr),
|
||||||
|
// TODO: other object types
|
||||||
|
_ => return Err(SysError::InvalidArgument),
|
||||||
|
};
|
||||||
|
|
||||||
|
slot.set(new_cap);
|
||||||
|
// TODO: insert into linked list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update free_offset
|
||||||
|
self.set_free_offset(start_offset + objs_size);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user