[wip] experiment with caps representation
This commit is contained in:
parent
8a5ef112be
commit
333dece260
|
@ -33,13 +33,57 @@ version = "0.3.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "mashup"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e81a2ed9c9879f92f2443fec9e6326c673b0dba3190c902b0371fd1387c4289"
|
||||
dependencies = [
|
||||
"mashup-impl",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mashup-impl"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "875a96a1b7ed5bdb76b8676b98a15d8888ee15e68833bb7a42897b59a9a93047"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2 0.4.30",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b"
|
||||
dependencies = [
|
||||
"proc-macro-hack-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack-impl"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -54,7 +98,7 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2 1.0.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -88,7 +132,7 @@ version = "0.6.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
@ -99,9 +143,9 @@ version = "1.0.48"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -110,6 +154,12 @@ version = "0.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f521a79accce68c417c9c77ce22108056b626126da1932f7e2e9b5bbffee0cea"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
|
@ -136,6 +186,7 @@ dependencies = [
|
|||
"bitflags",
|
||||
"cfg-if",
|
||||
"cortex-a",
|
||||
"mashup",
|
||||
"qemu-exit",
|
||||
"r0",
|
||||
"register",
|
||||
|
|
|
@ -34,3 +34,4 @@ bit_field = "0.10.0"
|
|||
bitflags = "1.2.1"
|
||||
cfg-if = "1.0"
|
||||
snafu = { version = "0.6", default-features = false }
|
||||
mashup = "0.1.9" # @todo replace with paste 1.0
|
||||
|
|
|
@ -13,6 +13,8 @@ use {
|
|||
cortex_a::{asm, regs::*},
|
||||
};
|
||||
|
||||
//use crate::arch::caps::{CapNode, Capability};
|
||||
|
||||
// Stack placed before first executable instruction
|
||||
const STACK_START: u64 = 0x0008_0000; // Keep in sync with linker script
|
||||
|
||||
|
@ -218,3 +220,176 @@ pub unsafe extern "C" fn _boot_cores() -> ! {
|
|||
// if not core0 or not EL3/EL2/EL1, infinitely wait for events
|
||||
endless_sleep()
|
||||
}
|
||||
|
||||
/*
|
||||
// caps and mem regions init
|
||||
|
||||
enum KernelInitError {}
|
||||
|
||||
fn map_kernel_window() {}
|
||||
|
||||
fn init_cpu() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn init_plat() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn arch_init_freemem() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn create_domain_cap() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn init_irqs() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn create_bootinfo_cap() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn create_asid_pool_for_initial_thread() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn create_idle_thread() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn clean_invalidate_l1_caches() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn create_initial_thread() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn init_core_state(_: Result<(), KernelInitError>) -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn create_untypeds() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn finalise_bootinfo() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn invalidate_local_tlb() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn lock_kernel_node() -> Result<(), KernelInitError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn schedule() {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn activate_thread() {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[link_section = ".text.boot"]
|
||||
// #[used]
|
||||
fn try_init_kernel() -> Result<(), KernelInitError> {
|
||||
map_kernel_window();
|
||||
init_cpu()?;
|
||||
init_plat()?;
|
||||
arch_init_freemem()?;
|
||||
|
||||
let root_capnode_cap = create_root_capnode();
|
||||
create_domain_cap(root_capnode_cap);
|
||||
init_irqs(root_capnode_cap);
|
||||
|
||||
//fill in boot info and
|
||||
create_bootinfo_cap();
|
||||
|
||||
let it_asid_pool_cap = create_asid_pool_for_initial_thread(root_capnode_cap);
|
||||
create_idle_thread();
|
||||
|
||||
/* Before creating the initial thread (which also switches to it)
|
||||
* we clean the cache so that any page table information written
|
||||
* as a result of calling create_frames_of_region will be correctly
|
||||
* read by the hardware page table walker */
|
||||
clean_invalidate_l1_caches();
|
||||
|
||||
let it = create_initial_thread(root_capnode_cap);
|
||||
|
||||
init_core_state(it);
|
||||
|
||||
create_untypeds(root_capnode_cap);
|
||||
|
||||
finalise_bootinfo();
|
||||
|
||||
clean_invalidate_l1_caches();
|
||||
invalidate_local_tlb();
|
||||
|
||||
// grab kernel lock before returning
|
||||
lock_kernel_node();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_init_kernel_secondary_core() -> Result<(), KernelInitError>
|
||||
{
|
||||
init_cpu();
|
||||
|
||||
/* Enable per-CPU timer interrupts */
|
||||
maskInterrupt(false, KERNEL_TIMER_IRQ);
|
||||
|
||||
lock_kernel_node;
|
||||
|
||||
ksNumCPUs++; // increase global cpu counter - this should be done differently?
|
||||
|
||||
init_core_state(SchedulerAction_ResumeCurrentThread);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_kernel() {
|
||||
try_init_kernel()?;
|
||||
// or for AP:
|
||||
// try_init_kernel_secondary_core();
|
||||
schedule();
|
||||
activate_thread();
|
||||
}
|
||||
|
||||
const CONFIG_ROOT_CAPNODE_SIZE_BITS: usize = 12;
|
||||
const wordBits: usize = 64;
|
||||
|
||||
fn create_root_capnode() -> Capability // Attr(BOOT_CODE)
|
||||
{
|
||||
// write the number of root CNode slots to global state
|
||||
boot_info.max_slot_pos = 1 << CONFIG_ROOT_CAPNODE_SIZE_BITS; // 12 bits => 4096 slots
|
||||
|
||||
// seL4_SlotBits = 32 bytes per entry, 4096 entries =>
|
||||
// create an empty root CapNode
|
||||
// this goes into the kernel startup/heap memory (one of the few items that kernel DOES allocate).
|
||||
let region_size = core::mem::size_of::<Capability> * boot_info.max_slot_pos; // 12 + 5 => 131072 (128Kb)
|
||||
let pptr = alloc_region(region_size); // GlobalAllocator::alloc_zeroed instead?
|
||||
if pptr.is_none() {
|
||||
println!("Kernel init failing: could not create root capnode");
|
||||
return Capability(NullCap::Type::value);
|
||||
}
|
||||
let Some(pptr) = pptr;
|
||||
memzero(pptr, region_size); // CTE_PTR(pptr) ?
|
||||
|
||||
// transmute into a type? (you can use ptr.write() to just write a type into memory location)
|
||||
|
||||
let cap = CapNode::new_root(pptr);
|
||||
|
||||
// this cnode contains a cap to itself...
|
||||
/* write the root CNode cap into the root CNode */
|
||||
// @todo rootCapNode.write_slot(CapInitThreadCNode, cap); -- where cap and rootCapNode are synonyms!
|
||||
write_slot(SLOT_PTR(pptr, seL4_CapInitThreadCNode), cap);
|
||||
|
||||
cap // reference to pptr is here
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* 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).
|
||||
|
||||
// ☐ 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 core::{convert::TryFrom, fmt};
|
||||
use mashup::*;
|
||||
use register::LocalRegisterCopy;
|
||||
|
||||
//==================
|
||||
// Caps definitions
|
||||
//==================
|
||||
|
||||
register_bitfields! [u128,
|
||||
NullCap [
|
||||
Type OFFSET(64) NUMBITS(5) [
|
||||
value = 0
|
||||
]
|
||||
],
|
||||
UntypedCap [
|
||||
FreeIndex OFFSET(0) NUMBITS(48) [],
|
||||
IsDevice OFFSET(57) NUMBITS(1) [],
|
||||
BlockSize OFFSET(58) NUMBITS(6) [],
|
||||
Type OFFSET(64) NUMBITS(5) [
|
||||
value = 2
|
||||
],
|
||||
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
|
||||
//field_high mdbNext 46
|
||||
//field mdbRevocable 1
|
||||
//field mdbFirstBadged 1
|
||||
//
|
||||
//field mdbPrev 64
|
||||
//}
|
||||
|
||||
register_bitfields! [u128,
|
||||
CapDerivationNode [
|
||||
// Next CTE node address -- per cteInsert this is address of the entire CTE slot
|
||||
Next OFFSET(16) NUMBITS(46) [], // 4-bytes-aligned, size of canonical phys address is 48 bits
|
||||
Revocable OFFSET(62) NUMBITS(1) [
|
||||
Disable = 0,
|
||||
Enable = 1
|
||||
],
|
||||
FirstBadged OFFSET(63) NUMBITS(1) [
|
||||
Disable = 0,
|
||||
Enable = 1
|
||||
],
|
||||
// Prev CTE node address -- per cteInsert this is address of the entire CTE slot
|
||||
Prev OFFSET(64) NUMBITS(64) []
|
||||
]
|
||||
];
|
||||
|
||||
/// 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),*) => {
|
||||
mashup! {
|
||||
$(
|
||||
m[$name "Capability"] = $name Capability;
|
||||
m[$name "Cap"] = $name Cap;
|
||||
)*
|
||||
}
|
||||
|
||||
m! {
|
||||
$(
|
||||
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()
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
capdefs! {
|
||||
Null, Untyped, Endpoint,
|
||||
Notification, Reply, CapNode,
|
||||
Thread, IrqControl, IrqHandler,
|
||||
Zombie, Domain, Resume,
|
||||
Frame, PageTable, PageDirectory,
|
||||
PageUpperDirectory, PageGlobalDirectory,
|
||||
AsidControl, AsidPool
|
||||
}
|
||||
|
||||
// * 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 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_node = DerivationTreeNode::empty()
|
||||
.set_revocable(true)
|
||||
.set_first_badged(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl NullCapability {
|
||||
pub fn new() -> NullCapability {
|
||||
NullCapability(LocalRegisterCopy::new(u128::from(NullCap::Type::value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl CapNodeCapability {
|
||||
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()),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for CapDerivationNode
|
||||
#[derive(Clone)]
|
||||
pub struct DerivationTreeNode(LocalRegisterCopy<u128, CapDerivationNode::Register>);
|
||||
|
||||
pub enum DerivationTreeError {
|
||||
InvalidPrev,
|
||||
}
|
||||
|
||||
impl DerivationTreeNode {
|
||||
fn empty() -> Self {
|
||||
Self(LocalRegisterCopy::new(0))
|
||||
}
|
||||
|
||||
// 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) as *const CapTableEntry;
|
||||
(*ptr).clone()
|
||||
}
|
||||
|
||||
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() })
|
||||
}
|
||||
}
|
||||
|
||||
fn set_first_badged(mut self, enable: bool) -> Self {
|
||||
self.0.modify(if enable {
|
||||
CapDerivationNode::FirstBadged::Enable
|
||||
} else {
|
||||
CapDerivationNode::FirstBadged::Disable
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
fn set_revocable(mut self, enable: bool) -> Self {
|
||||
self.0.modify(if enable {
|
||||
CapDerivationNode::Revocable::Enable
|
||||
} else {
|
||||
CapDerivationNode::Revocable::Disable
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// -- 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;
|
||||
#[derive(Clone)]
|
||||
pub struct CapTableEntry {
|
||||
capability: u128,
|
||||
derivation_node: DerivationTreeNode,
|
||||
}
|
||||
|
||||
impl fmt::Debug for CapTableEntry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:x}", self.capability); // @todo
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CapTableEntry {
|
||||
// Temporary for testing:
|
||||
fn empty() -> CapTableEntry {
|
||||
CapTableEntry {
|
||||
capability: 0,
|
||||
derivation_node: DerivationTreeNode::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The different libraries seem to target different use-cases, though. For example, `snafu` with its
|
||||
// strongly typed errors and contexts seems to be a good fit for libraries. On the other hand,
|
||||
// anyhow with its focus on the type-erased Error and on creating string errors and contexts seems
|
||||
// to be more useful for applications. After all, errors produced by libraries need to be understood
|
||||
// by other code, errors produced by executables need to be understood by humans.
|
||||
// -- https://lukaskalbertodt.github.io/2019/11/14/thoughts-on-error-handling-in-rust.html
|
||||
// @see also https://blog.yoshuawuyts.com/error-handling-survey/
|
||||
// @see also https://www.reddit.com/r/rust/comments/dfs1zk/2019_q4_error_patterns_snafu_vs_errderive_anyhow/
|
||||
#[derive(Debug)]
|
||||
pub enum CapError {
|
||||
CannotCreate,
|
||||
InvalidCapabilityType,
|
||||
}
|
||||
|
||||
// @note src and dest are swapped here, compared to seL4 api
|
||||
/*
|
||||
struct CapNodePath {
|
||||
index: u32,
|
||||
depth: u32,
|
||||
}
|
||||
|
||||
struct CapNodeRootedPath {
|
||||
root: CapNode,
|
||||
path: CapNodePath,
|
||||
}
|
||||
|
||||
// @todo just use CapNodeCap
|
||||
//struct CapNodeConfig {
|
||||
// guard: u32,
|
||||
// guard_size: u32,
|
||||
//}
|
||||
|
||||
impl CapNode {
|
||||
fn mint(
|
||||
src: CapNodeRootedPath,
|
||||
dest: CapNodePath,
|
||||
rights: CapRights,
|
||||
badge: Badge,
|
||||
) -> Result<(), CapError> {
|
||||
unimplemented!();
|
||||
}
|
||||
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> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn cancel_badged_sends(path: CapNodePath) -> Result<(), CapError> {
|
||||
unimplemented!();
|
||||
}
|
||||
}*/
|
||||
|
||||
//struct CapSpace {} -- capspace is collection of capnodes in a single address space?
|
||||
//impl CapNode for CapSpace {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test_case]
|
||||
fn first_capability_derivation_has_no_prev_link() {}
|
||||
}
|
|
@ -8,6 +8,8 @@
|
|||
use cortex_a::asm;
|
||||
|
||||
mod boot;
|
||||
pub mod caps;
|
||||
pub use self::caps::*;
|
||||
#[cfg(feature = "jtag")]
|
||||
pub mod jtag;
|
||||
pub mod memory;
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
*/
|
||||
// The basic services Vesper provides are as follows:
|
||||
//
|
||||
// * _Threads_ are an abstraction of CPU execution that supports running software;
|
||||
// * _Address spaces_ are virtual memory spaces that each contain an application.
|
||||
// Applications are limited to accessing memory in their address space;
|
||||
// * _Inter-process communication (IPC)_ via endpoints allows threads to communicate using
|
||||
// message passing;
|
||||
// * _Events_ provide a non-blocking signalling mechanism similar to counting semaphores;
|
||||
// * _Device primitives_ allow device drivers to be implemented as unprivileged applications.
|
||||
// The kernel exports hardware device interrupts via IPC messages; and
|
||||
// * _Capability spaces_ store capabilities (i.e., access rights) to kernel services along with
|
||||
// their book-keeping information.
|
||||
|
||||
//================
|
||||
// Kernel objects
|
||||
//================
|
||||
|
||||
register_bitfields! {u128,
|
||||
Endpoint [
|
||||
QueueHead OFFSET(0) NUMBITS(64) [],
|
||||
QueueTail OFFSET(80) NUMBITS(46) [],
|
||||
State OFFSET(126) NUMBITS(2) [
|
||||
Idle = 00b,
|
||||
Send = 01b,
|
||||
Recv = 10b,
|
||||
],
|
||||
],
|
||||
}
|
||||
|
||||
// @todo replace with Event
|
||||
register_bitfields! {u256,
|
||||
Notification [
|
||||
BoundTCB OFFSET(16) NUMBITS(48) [],
|
||||
MsgId OFFSET(64) NUMBITS(64) [],
|
||||
QueueHead OFFSET(144) NUMBITS(48) [],
|
||||
QueueTail OFFSET(192) NUMBITS(48) [],
|
||||
State OFFSET(254) NUMBITS(2) [
|
||||
Idle = 00b,
|
||||
Waiting = 01b,
|
||||
Active = 10b,
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
// TCB (Thread)
|
||||
// +--VirtSpace
|
||||
// +--CapSpace
|
||||
|
||||
|
||||
enum MemoryKind {
|
||||
General,
|
||||
Device,
|
||||
}
|
||||
|
||||
|
||||
// The source of all available memory, device or general.
|
||||
// Boot code reserves kernel memory and initial mapping allocations (4 pages probably - on rpi3? should be platform-dependent).
|
||||
// The rest is converted to untypeds with appropriate kind and given away to start thread.
|
||||
|
||||
trait Untyped {
|
||||
// Uses T::SIZE_BITS to properly size the resulting object
|
||||
// (`where T: KernelObject`)
|
||||
// in some cases size_bits must be passed as argument though...
|
||||
fn retype<T>(target_cap: CapNodeRootedPath, target_cap_offset: usize, num_objects: usize) -> Result<()>; // @todo return an array of caps?
|
||||
}
|
||||
|
||||
// MMU
|
||||
|
||||
// ActivePageTable (--> impl VirtSpace for ActivePageTable etc...)
|
||||
// * translate(VirtAddr)->PhysAddr
|
||||
// * translate_page(Page)->PhysAddr
|
||||
// * map_to(Page, PhysFrame, Flags, FrameAllocator)->()
|
||||
// * map(Page, Flags, FrameAllocator)->()
|
||||
// * identity_map(PhysFrame, Flags, FrameAllocator)->()
|
||||
// * unmap(Page, FrameAllocator)->()
|
||||
|
||||
trait VirtSpace {
|
||||
fn map(virt_space: VirtSpace/*Cap*/, vaddr: VirtAddr, rights: CapRights, attr: VMAttributes) -> Result<()>; /// ??
|
||||
fn unmap() -> Result<()>; /// ??
|
||||
fn remap(virt_space: VirtSpace/*Cap*/, rights: CapRights, attr: VMAttributes) -> Result<()>; /// ??
|
||||
fn get_address() -> Result<PhysAddr>;///??
|
||||
}
|
||||
|
||||
// ARM AArch64 processors have a four-level page-table structure, where the
|
||||
// VirtSpace is realised as a PageGlobalDirectory. All paging structures are
|
||||
// indexed by 9 bits of the virtual address.
|
||||
|
||||
// AArch64 page hierarchy:
|
||||
//
|
||||
// PageGlobalDirectory (L0) -- aka VirtSpace
|
||||
// +--PageUpperDirectory (L1)
|
||||
// +--Page<Size1GiB> -- aka HugePage
|
||||
// | or
|
||||
// +--PageDirectory (L2)
|
||||
// +--Page<Size2MiB> -- aka LargePage
|
||||
// | or
|
||||
// +--PageTable (L3)
|
||||
// +--Page<Size4KiB> -- aka Page
|
||||
|
||||
|
||||
trait PageCacheManagement {
|
||||
// Cache data management.
|
||||
/// Cleans the data cache out to RAM.
|
||||
/// The start and end are relative to the page being serviced.
|
||||
fn clean_data(start_offset: usize, end_offset: usize) -> Result<()>;
|
||||
/// Clean and invalidates the cache range within the given page.
|
||||
/// The range will be flushed out to RAM. The start and end are relative
|
||||
/// to the page being serviced.
|
||||
fn clean_invalidate_data(start_offset: usize, end_offset: usize) -> Result<()>;
|
||||
/// Invalidates the cache range within the given page.
|
||||
/// The start and end are relative to the page being serviced and should
|
||||
/// be aligned to a cache line boundary where possible. An additional
|
||||
/// clean is performed on the outer cache lines if the start and end are
|
||||
/// not aligned, to clean out the bytes between the requested and
|
||||
/// the cache line boundary.
|
||||
fn invalidate_data(start_offset: usize, end_offset: usize) -> Result<()>;
|
||||
/// Cleans data lines to point of unification, invalidates
|
||||
/// corresponding instruction lines to point of unification, then
|
||||
/// invalidates branch predictors.
|
||||
/// The start and end are relative to the page being serviced.
|
||||
fn unify_instruction_cache(start_offset: usize, end_offset: usize) -> Result<()>;
|
||||
}
|
||||
|
||||
// ARM
|
||||
struct Page {}
|
||||
|
||||
impl Page {
|
||||
// VirtSpace-like interface.
|
||||
/// Get the physical address of the underlying frame.
|
||||
fn get_address() -> Result<PhysAddr>;
|
||||
fn map(virt_space: VirtSpace/*Cap*/, vaddr: VirtAddr, rights: CapRights, attr: VMAttributes) -> Result<()>;
|
||||
/// Changes the permissions of an existing mapping.
|
||||
fn remap(virt_space: VirtSpace/*Cap*/, rights: CapRights, attr: VMAttributes) -> Result<()>;
|
||||
fn unmap() -> Result<()>;
|
||||
// MMIO space.
|
||||
fn map_io(iospace: IoSpace/*Cap*/, rights: CapRights, ioaddr: VirtAddr) -> Result<()>;
|
||||
}
|
||||
|
||||
impl PageCacheManagement for Page {
|
||||
fn clean_data(start_offset: usize, end_offset: usize) -> _ {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn clean_invalidate_data(start_offset: usize, end_offset: usize) -> _ {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn invalidate_data(start_offset: usize, end_offset: usize) -> _ {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn unify_instruction_cache(start_offset: usize, end_offset: usize) -> _ {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// ARM
|
||||
// L3 tables
|
||||
struct PageTable {}
|
||||
|
||||
impl PageTable {
|
||||
fn map(virt_space: VirtSpace/*Cap*/, vaddr: VirtAddr, attr: VMAttributes) -> Result<()>;
|
||||
fn unmap() -> Result<()>;
|
||||
}
|
||||
|
||||
// AArch64 - probably just impl some Mapping trait for these "structs"?
|
||||
// L2 table
|
||||
struct PageDirectory {}
|
||||
|
||||
impl PageDirectory {
|
||||
fn map(pud: PageUpperDirectory/*Cap*/, vaddr: VirtAddr, attr: VMAttributes) -> Result<()>;
|
||||
fn unmap() -> Result<()>;
|
||||
}
|
||||
|
||||
// L1 table
|
||||
struct PageUpperDirectory {}
|
||||
|
||||
impl PageUpperDirectory {
|
||||
fn map(pgd: PageGlobalDirectory/*Cap*/, vaddr: VirtAddr, attr: VMAttributes) -> Result<()>;
|
||||
fn unmap() -> Result<()>;
|
||||
}
|
||||
|
||||
// L0 table
|
||||
struct PageGlobalDirectory {
|
||||
// @todo should also impl VirtSpace to be able to map shit?
|
||||
// or the Page's impl will do this?
|
||||
}
|
||||
|
||||
impl PageCacheManagement for PageGlobalDirectory {
|
||||
fn clean_data(start_offset: usize, end_offset: usize) -> _ {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn clean_invalidate_data(start_offset: usize, end_offset: usize) -> _ {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn invalidate_data(start_offset: usize, end_offset: usize) -> _ { todo!() }
|
||||
|
||||
fn unify_instruction_cache(start_offset: usize, end_offset: usize) -> _ {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// implemented for x86 and arm
|
||||
trait ASIDPool {
|
||||
fn assign(virt_space: VirtSpace/*Cap*/) -> Result<()>;
|
||||
}
|
||||
|
||||
// implemented for x86 and arm
|
||||
trait ASIDControl {
|
||||
fn make_pool(untyped: Untyped, target_cap_space_cap: CapNodeRootedPath) -> Result<()>;
|
||||
}
|
||||
|
||||
// Allocation details
|
||||
|
||||
// 1. should be possible to map non-SAS style
|
||||
// 2. should be easy to map SAS style
|
||||
// 3. should not allocate any memory dynamically
|
||||
// ^ problem with the above API is FrameAllocator
|
||||
// ^ clients should supply their own memory for frames...
|
||||
|
||||
|
||||
// https://github.com/seL4/seL4_libs/tree/master/libsel4allocman
|
||||
|
||||
// Allocation overview
|
||||
|
||||
// Allocation is complex due to the circular dependencies that exist on allocating resources. These dependencies are loosely described as
|
||||
|
||||
// Capability slots: Allocated from untypeds, book kept in memory.
|
||||
// Untypeds / other objects (including frame objects): Allocated from other untypeds, into capability slots, book kept in memory.
|
||||
// memory: Requires frame object.
|
||||
|
||||
// Other seL4-like kernel objects and their interfaces:
|
||||
|
||||
trait Thread {
|
||||
// Effectively, SetSpace followed by SetIPCBuffer.
|
||||
fn configure(fault_endpoint: CapNode, cap_space_root: CapNode, cap_space_root_data: CapNodeConfig, virt_space_root: CapNode, virt_space_root_data: ??, ipc_buffer_frame: CapNode, ipc_buffer_offset: usize) -> Result<()>;
|
||||
fn set_space(fault_endpoint: CapNode, cap_space_root: CapNode, cap_space_root_data: CapNodeConfig, virt_space_root: CapNode, virt_space_root_data: ??) -> Result<()>;
|
||||
fn configure_single_stepping(bp_num: u16, num_insns): Result<SingleStepping>;
|
||||
fn get_breakpoint(bp_num: u16) -> Result<BreakpointInfo>;
|
||||
fn set_breakpoint(bp_num: u16, bp: BreakpointInfo) -> Result<()>;
|
||||
fn unset_breakpoint(bp_num: u16) -> Result<()>;
|
||||
fn suspend() -> Result<()>;
|
||||
fn resume() -> Result<()>;
|
||||
fn copy_registers(source: TCB/*Cap*/, suspend_source: bool, resume_target: bool, transfer_frame_regs: bool, transfer_integer_regs: bool, arch_flags: u8) -> Result<()>;
|
||||
fn read_registers(suspend_source: bool, arch_flags: u8, num_regs: u16, register_context: &mut ArchRegisterContext) -> Result<()>;
|
||||
fn write_registers(resume_target: bool, arch_flags: u8, num_regs: u16, register_context: &ArchRegisterContext) -> Result<()>;
|
||||
fn bind_notification(notification: CapNode) -> Result<()>;
|
||||
fn unbind_notification() -> Result<()>;
|
||||
fn set_priority(authority: TCB/*Cap*/, priority: u32) -> Result<()>;
|
||||
fn set_mc_priority(authority: TCB/*Cap*/, mcp: u32) -> Result<()>;
|
||||
fn set_sched_params(authority: TCB/*Cap*/, mcp: u32, priority: u32) -> Result<()>;
|
||||
fn set_affinity(affinity: u64) -> Result<()>;
|
||||
fn set_ipc_buffer(ipc_buffer_frame: CapNode, ipc_buffer_offset: usize) -> Result<()>;
|
||||
// Arch-specific
|
||||
fn set_tls_base(tls_base: usize) -> Result<()>;
|
||||
// virtualized - x86-specific
|
||||
fn set_ept_root(eptpml: X86::EPTPML4) -> Result<()>;
|
||||
}
|
||||
|
||||
// @todo <<SchedContext>>
|
||||
|
||||
struct TCB {}
|
||||
|
||||
impl Thread for TCB {}
|
||||
impl KernelObject for TCB {
|
||||
const SIZE_BITS: usize = 12;
|
||||
}
|
||||
|
||||
trait Notification {
|
||||
fn signal(dest: Cap);
|
||||
fn wait(src: Cap) -> Result<Option<&Badge>>;
|
||||
fn poll(cap: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
}
|
||||
|
||||
trait IRQHandler {
|
||||
fn set_notification(notification: CapNode) -> Result<()>;
|
||||
fn ack() -> Result<()>;
|
||||
fn clear() -> Result<()>;
|
||||
}
|
||||
|
||||
trait IRQControl {
|
||||
fn get(irq: u32, dest: CapNodeRootedPath) -> Result<()>;
|
||||
// ARM?
|
||||
fn get_trigger();
|
||||
fn get_trigger_core();
|
||||
}
|
||||
|
||||
// Syscalls (kernel API)
|
||||
trait API {
|
||||
fn send(cap: Cap, msg_info: MessageInfo);
|
||||
// Wait for message, when it is received,
|
||||
// return object Badge and block caller on `reply`.
|
||||
fn recv(src: Cap, reply: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
fn call(cap: Cap, msg_info: MessageInfo) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
fn reply(msg_info: MessageInfo);
|
||||
fn nb_send(dest: Cap, msg_info: MessageInfo);
|
||||
// As Recv but invoke `reply` first.
|
||||
fn reply_recv(src: Cap, reply: Cap, msg_info: MessageInfo) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
// As ReplyRecv but invoke `dest` not `reply`.
|
||||
fn nb_send_recv(dest: Cap, msg_info: MessageInfo, src: Cap, reply: Cap) -> Result<(MessageInfo, Options<&Badge>)>;
|
||||
fn nb_recv(src: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
// As NBSendRecv, with no reply. Donation is not possible.
|
||||
fn nb_send_wait(cap: Cap, msg_info: MessageInfo, src: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
// As per Recv, but donation not possible.
|
||||
fn wait(src: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
fn r#yield();
|
||||
// Plus some debugging calls...
|
||||
}
|
||||
|
||||
struct Kernel {} // Nucleus, actually...
|
||||
impl API for Kernel {}
|
||||
|
||||
trait DomainSet {
|
||||
// ??
|
||||
fn set(domain, thread: TCB);
|
||||
}
|
||||
|
||||
// Virtualisation
|
||||
// ARM
|
||||
trait VCPU {
|
||||
fn inject_i_r_q(virq: u16, priority: u8, group: u8, index: u8) -> Result<()>;
|
||||
fn read_registers();
|
||||
fn write_registers();
|
||||
fn set_tcb();
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(crate::tests::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
#![recursion_limit = "4096"] // for mashup! macro
|
||||
#![deny(missing_docs)]
|
||||
#![deny(warnings)]
|
||||
|
||||
|
@ -161,6 +162,11 @@ pub fn kmain() -> ! {
|
|||
#[cfg(test)]
|
||||
test_main();
|
||||
|
||||
/*
|
||||
try_init_kernel().expect("Failed to init kernel");*/
|
||||
// schedule();
|
||||
// activate_thread();
|
||||
|
||||
command_prompt();
|
||||
|
||||
reboot()
|
||||
|
@ -231,6 +237,70 @@ fn reboot() -> ! {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum KernelInitError {
|
||||
CapabilityCreationFailed,
|
||||
}
|
||||
|
||||
#[link_section = ".text.boot"]
|
||||
fn try_init_kernel() -> Result<(), KernelInitError> {
|
||||
let root_capnode_cap = create_root_capnode();
|
||||
if root_capnode_cap.is_err() {
|
||||
return Err(KernelInitError::CapabilityCreationFailed);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const CONFIG_ROOT_CAPNODE_SIZE_BITS: usize = 12;
|
||||
const WORD_BITS: usize = 64;
|
||||
|
||||
enum BootInfoCaps {
|
||||
InitThreadCapNode = 1,
|
||||
}
|
||||
|
||||
impl From<BootInfoCaps> for usize {
|
||||
fn from(val: BootInfoCaps) -> usize {
|
||||
val as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[link_section = ".text.boot"]
|
||||
fn create_root_capnode() -> Result<CapNodeCapability, caps::CapError> {
|
||||
// write the number of root CNode slots to global state
|
||||
BOOT_INFO.lock(|bi| bi.max_slot_pos = 1 << CONFIG_ROOT_CAPNODE_SIZE_BITS); // 12 bits => 4096 slots
|
||||
|
||||
// seL4_SlotBits = 32 bytes per entry, 4096 entries =>
|
||||
// create an empty root CapNode
|
||||
// this goes into the kernel startup/heap memory (one of the few items that kernel DOES allocate).
|
||||
let region_size = core::mem::size_of::<CapTableEntry>() * BOOT_INFO.lock(|bi| bi.max_slot_pos); // 12 + 5 => 131072 (128Kb)
|
||||
let pptr = BOOT_INFO.lock(|bi| bi.alloc_region(region_size)); // GlobalAllocator::alloc_zeroed instead?
|
||||
let pptr = match pptr {
|
||||
Ok(pptr) => pptr,
|
||||
Err(_) => {
|
||||
println!("Kernel init failed: could not create root capnode");
|
||||
return Err(caps::CapError::CannotCreate);
|
||||
}
|
||||
};
|
||||
// @todo lifetime of slice -- slice here only to zero out memory
|
||||
let slice =
|
||||
unsafe { core::slice::from_raw_parts_mut(pptr.as_u64() as *mut u8, region_size as usize) };
|
||||
for byte in slice.iter_mut() {
|
||||
*byte = 0;
|
||||
} // @todo wtf
|
||||
|
||||
// transmute into a type? (you can use ptr.write() to just write a type into memory location)
|
||||
|
||||
let cap = CapNodeCapability::new_root(pptr.as_u64());
|
||||
use core::convert::TryFrom;
|
||||
let mut cap_node = CapNodeCapability::try_from(cap.as_u128()).unwrap();
|
||||
|
||||
// this cnode contains a cap to itself...
|
||||
/* write the root CNode cap into the root CNode */
|
||||
cap_node.write_slot(usize::from(BootInfoCaps::InitThreadCapNode), &cap);
|
||||
|
||||
Ok(cap_node) // reference to pptr is here
|
||||
}
|
||||
|
||||
fn check_display_init() {
|
||||
display_graphics()
|
||||
.map_err(|e| {
|
||||
|
|
Loading…
Reference in New Issue