[wip] compile fixes -- to split

This commit is contained in:
Berkus Decker 2021-01-04 03:36:33 +02:00
parent 0e1c0e45f7
commit b594552f23
27 changed files with 382 additions and 208 deletions

View File

@ -8,42 +8,50 @@
//! Arch-specific kernel ABI decodes syscall invocations and calls API functions to perform actual //! Arch-specific kernel ABI decodes syscall invocations and calls API functions to perform actual
//! operations. //! operations.
use vesper_user::SysCall as SysCall; use {
vesper_user::SysCall as SysCall,
crate::println,
snafu::Snafu,
crate::arch,
crate::arch::memory::{PhysAddr, VirtAddr},
crate::objects::thread::ThreadState,
crate::caps::{ReplyCapability, NullCapability},
};
// Syscalls (kernel API) // Syscalls (kernel API)
trait API { trait API {
// Three below (send, nb_send, call) are "invocation" syscalls. // Three below (send, nb_send, call) are "invocation" syscalls.
fn send(cap: Cap, msg_info: MessageInfo); // fn send(cap: Cap, msg_info: MessageInfo);
fn nb_send(dest: Cap, msg_info: MessageInfo); // fn nb_send(dest: Cap, msg_info: MessageInfo);
fn call(cap: Cap, msg_info: MessageInfo) -> Result<(MessageInfo, Option<&Badge>)>; // fn call(cap: Cap, msg_info: MessageInfo) -> Result<(MessageInfo, Option<&Badge>)>;
// Wait for message, when it is received, // Wait for message, when it is received,
// return object Badge and block caller on `reply`. // return object Badge and block caller on `reply`.
fn recv(src: Cap, reply: Cap) -> Result<(MessageInfo, Option<&Badge>)>; // fn recv(src: Cap, reply: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
fn reply(msg_info: MessageInfo); // fn reply(msg_info: MessageInfo);
// As Recv but invoke `reply` first. // As Recv but invoke `reply` first.
fn reply_recv( // fn reply_recv(
src: Cap, // src: Cap,
reply: Cap, // reply: Cap,
msg_info: MessageInfo, // msg_info: MessageInfo,
) -> Result<(MessageInfo, Option<&Badge>)>; // ) -> Result<(MessageInfo, Option<&Badge>)>;
fn nb_recv(src: Cap) -> Result<(MessageInfo, Option<&Badge>)>; // fn nb_recv(src: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
fn r#yield(); // fn r#yield();
// -- end of default seL4 syscall list -- // -- end of default seL4 syscall list --
// As ReplyRecv but invoke `dest` not `reply`. // As ReplyRecv but invoke `dest` not `reply`.
fn nb_send_recv( // fn nb_send_recv(
dest: Cap, // dest: Cap,
msg_info: MessageInfo, // msg_info: MessageInfo,
src: Cap, // src: Cap,
reply: Cap, // reply: Cap,
) -> Result<(MessageInfo, Options<&Badge>)>; // ) -> Result<(MessageInfo, Options<&Badge>)>;
// As NBSendRecv, with no reply. Donation is not possible. // As NBSendRecv, with no reply. Donation is not possible.
fn nb_send_wait( // fn nb_send_wait(
cap: Cap, // cap: Cap,
msg_info: MessageInfo, // msg_info: MessageInfo,
src: Cap, // src: Cap,
) -> Result<(MessageInfo, Option<&Badge>)>; // ) -> Result<(MessageInfo, Option<&Badge>)>;
// As per Recv, but donation not possible. // As per Recv, but donation not possible.
fn wait(src: Cap) -> Result<(MessageInfo, Option<&Badge>)>; // fn wait(src: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
// Plus some debugging calls... // Plus some debugging calls...
} }
@ -175,29 +183,31 @@ fn handle_invocation(is_call: bool, is_blocking: bool) -> Result<()> {
Ok(()) Ok(())
} }
fn handle_receive(is_blocking: bool) { fn handle_receive(is_blocking: bool) -> Result<()> {
let endpoint_cap_ptr = KernelCurrentThread.get_register(capRegister); let endpoint_cap_ptr = KernelCurrentThread.get_register(capRegister);
let result = KernelCurrentThread.lookup_cap(endpoint_cap_ptr); let result = KernelCurrentThread.lookup_cap(endpoint_cap_ptr);
if result.is_err() { if result.is_err() {
KernelCurrentFault = Fault_CapFault::new(endpoint_cap_ptr, true); // KernelCurrentFault = Fault_CapFault::new(endpoint_cap_ptr, true);
handle_fault(KernelCurrentThread); handle_fault(KernelCurrentThread);
return Ok(()); return Ok(());
} }
match result.cap.get_type() { match result.capability.get_type() {
endpoint => , endpoint => Ok(()),
notification => , notification => Ok(()),
_ => fault, _ => Ok(()), //fault,
} }
} }
fn handle_reply() { fn handle_reply() {
let caller_slot = KernelCurrentThread.get_caller_slot(); let caller_slot = KernelCurrentThread.get_caller_slot();
let caller_cap = caller_slot.capability; let caller_cap = caller_slot.capability;
const reply_cap = ReplyCapability::Type::Type.value;
const null_cap = NullCapability::Type::Type.value;
match caller_cap.get_type() { match caller_cap.get_type() {
ReplyCap::Type.value => { reply_cap => {
// if (cap_reply_cap_get_capReplyMaster(callerCap)) { // if (cap_reply_cap_get_capReplyMaster(callerCap)) {
// break; // break;
// } // }
@ -205,7 +215,7 @@ fn handle_reply() {
// if(!(caller != ksCurThread)) _assert_fail("caller must not be the current thread", "src/api/syscall.c", 313, __FUNCTION__); // if(!(caller != ksCurThread)) _assert_fail("caller must not be the current thread", "src/api/syscall.c", 313, __FUNCTION__);
// do_reply_transfer(ksCurThread, caller, callerSlot); // do_reply_transfer(ksCurThread, caller, callerSlot);
}, },
NullCap::Type.value => { null_cap => {
println!("<<vesper[T{} \"{}\" @{}]: Attempted reply operation when no reply capability present.>>", KernelCurrentThread, KernelCurrentThread.name, KernelCurrentThread.get_restart_pc()); println!("<<vesper[T{} \"{}\" @{}]: Attempted reply operation when no reply capability present.>>", KernelCurrentThread, KernelCurrentThread.name, KernelCurrentThread.get_restart_pc());
}, },
_ => { _ => {
@ -293,53 +303,6 @@ fn handle_interrupt_entry() -> Result<()> {
Ok(()) Ok(())
} }
/* TCB: size 64 bytes + sizeof(arch_tcb_t) (aligned to nearest power of 2) */
struct TCB {
arch_tcb: arch::TCB,
state: ThreadState, // 12 bytes?
/* Notification that this TCB is bound to. If this is set, when this TCB waits on
* any sync endpoint, it may receive a signal from a Notification object.
* 4 bytes*/
// notification_t *tcbBoundNotification;
fault: Fault, // 8 bytes?
lookup_failure: LookupFault, // 8 bytes
/* Domain, 1 byte (packed to 4) */
domain: Domain,
/* maximum controlled priorioty, 1 byte (packed to 4) */
mcp: Priority,
/* Priority, 1 byte (packed to 4) */
priority: Priority,
/* Timeslice remaining, 4 bytes */
time_slice: u32,
/* Capability pointer to thread fault handler, 4 bytes */
fault_handler: CapPath,
/* userland virtual address of thread IPC buffer, 4 bytes */
ipc_buffer: VirtAddr,
/* Previous and next pointers for scheduler queues , 8 bytes */
sched_next: *mut TCB,
sched_prev: *mut TCB,
/* Previous and next pointers for endpoint and notification queues, 8 bytes */
ep_next: *mut TCB,
ep_prev: *mut TCB,
/* Use any remaining space for a thread name */
name: &str,
}
impl TCB {
fn get_register(...) {}
fn lookup_cap_and_slot() -> Result<()> {}
fn get_restart_pc() {}
fn lookup_ipc_buffer(some: bool) {}
fn lookup_extra_caps() -> Result<()> {}
fn get_state() -> ThreadState {}
fn set_state(state: ThreadState) {}
fn get_caller_slot() -> Slot {}
fn send_fault_ipc(&self) {}
fn replyFromKernel_success_empty() {}
fn replyFromKernel_error() {}
}
//handleSyscall(syscall) in the slowpath() //handleSyscall(syscall) in the slowpath()
// these are expressed in terms of // these are expressed in terms of
// handleInvocation(bool isCall, bool isBlocking) // handleInvocation(bool isCall, bool isBlocking)
@ -352,7 +315,7 @@ impl TCB {
// Call and ReplyRecv have fastpath handlers // Call and ReplyRecv have fastpath handlers
// the rest goes through slowpath // the rest goes through slowpath
// c_handle_syscall called directly from SWI vector entry // c_handle_syscall called directly from SVC vector entry
struct Scheduler; struct Scheduler;
@ -384,13 +347,13 @@ impl Scheduler {
fn activate_thread() {} fn activate_thread() {}
fn dequeue(thread: &mut TCB); fn dequeue(thread: &mut TCB) {}
fn append(thread: &mut TCB); fn append(thread: &mut TCB) {}
fn reschedule_required(); fn reschedule_required() {}
} }
struct Nucleus {} struct Nucleus {}
impl API for Nucleus { // impl API for Nucleus {
//... //...
} // }

View File

@ -14,4 +14,4 @@
/// Implements C ABI to easily parse passed in parameters. /// Implements C ABI to easily parse passed in parameters.
/// @todo Move this to aarch64-specific part. /// @todo Move this to aarch64-specific part.
#[no_mangle] #[no_mangle]
extern "C" pub(crate) syscall_entry() {} extern "C" pub(crate) syscall_entry(syscall_no: u64) {}

View File

@ -5,6 +5,7 @@
use { use {
crate::{ crate::{
arch::memory::{PhysAddr, VirtAddr, ASID},
capdef, capdef,
caps::{CapError, Capability}, caps::{CapError, Capability},
}, },
@ -37,19 +38,19 @@ capdef! { PageDirectory }
//===================== //=====================
impl PageDirectoryCapability { impl PageDirectoryCapability {
pub(crate) fn base_address() -> PhysAddr { pub(crate) fn base_address(&self) -> PhysAddr {
PhysAddr::new(self.0.read(PageDirectoryCap::BasePtr)) PhysAddr::new(self.0.read(PageDirectoryCap::BasePtr))
} }
pub(crate) fn is_mapped() -> bool { pub(crate) fn is_mapped(&self) -> bool {
self.0.read(PageDirectoryCap::IsMapped) == 1 self.0.read(PageDirectoryCap::IsMapped) == 1
} }
pub(crate) fn mapped_address() -> VirtAddr { pub(crate) fn mapped_address(&self) -> VirtAddr {
VirtAddr::new(self.0.read(PageDirectoryCap::MappedAddress)) VirtAddr::new(self.0.read(PageDirectoryCap::MappedAddress))
} }
pub(crate) fn mapped_asid() -> ASID { pub(crate) fn mapped_asid(&self) -> ASID {
self.0.read(PageDirectoryCap::MappedASID) self.0.read(PageDirectoryCap::MappedASID)
} }
} }

View File

@ -5,6 +5,7 @@
use { use {
crate::{ crate::{
arch::memory::{PhysAddr, VirtAddr, ASID},
capdef, capdef,
caps::{CapError, Capability}, caps::{CapError, Capability},
}, },
@ -36,19 +37,18 @@ capdef! { PageGlobalDirectory }
//===================== //=====================
impl PageGlobalDirectoryCapability { impl PageGlobalDirectoryCapability {
pub(crate) fn base_address() -> PhysAddr { pub(crate) fn base_address(&self) -> PhysAddr {
PhysAddr::new(self.0.read(PageGlobalDirectoryCap::BasePtr)) PhysAddr::new(self.0.read(PageGlobalDirectoryCap::BasePtr))
} }
pub(crate) fn is_mapped() -> bool { pub(crate) fn is_mapped(&self) -> bool {
self.0.read(PageGlobalDirectoryCap::IsMapped) == 1 self.0.read(PageGlobalDirectoryCap::IsMapped) == 1
} }
pub(crate) fn mapped_address() -> VirtAddr { // Global directory does not give access to mapped addresses,
VirtAddr::new(self.0.read(PageGlobalDirectoryCap::MappedAddress)) // instead, it links to lower page directory levels.
}
pub(crate) fn mapped_asid() -> ASID { pub(crate) fn mapped_asid(&self) -> ASID {
self.0.read(PageGlobalDirectoryCap::MappedASID) self.0.read(PageGlobalDirectoryCap::MappedASID)
} }
} }

View File

@ -5,6 +5,7 @@
use { use {
crate::{ crate::{
arch::memory::{PhysAddr, VirtAddr, ASID},
capdef, capdef,
caps::{CapError, Capability}, caps::{CapError, Capability},
}, },
@ -37,19 +38,19 @@ capdef! { PageTable }
//===================== //=====================
impl PageTableCapability { impl PageTableCapability {
pub(crate) fn base_address() -> PhysAddr { pub(crate) fn base_address(&self) -> PhysAddr {
PhysAddr::new(self.0.read(PageTableCap::BasePtr)) PhysAddr::new(self.0.read(PageTableCap::BasePtr))
} }
pub(crate) fn is_mapped() -> bool { pub(crate) fn is_mapped(&self) -> bool {
self.0.read(PageTableCap::IsMapped) == 1 self.0.read(PageTableCap::IsMapped) == 1
} }
pub(crate) fn mapped_address() -> VirtAddr { pub(crate) fn mapped_address(&self) -> VirtAddr {
VirtAddr::new(self.0.read(PageTableCap::MappedAddress)) VirtAddr::new(self.0.read(PageTableCap::MappedAddress))
} }
pub(crate) fn mapped_asid() -> ASID { pub(crate) fn mapped_asid(&self) -> ASID {
self.0.read(PageTableCap::MappedASID) self.0.read(PageTableCap::MappedASID)
} }
} }

View File

@ -16,6 +16,7 @@ pub mod mmu;
pub use addr::PhysAddr; pub use addr::PhysAddr;
pub use addr::VirtAddr; pub use addr::VirtAddr;
pub use addr::ASID;
// aarch64 granules and page sizes howto: // aarch64 granules and page sizes howto:
// https://stackoverflow.com/questions/34269185/simultaneous-existence-of-different-sized-pages-on-aarch64 // https://stackoverflow.com/questions/34269185/simultaneous-existence-of-different-sized-pages-on-aarch64

View File

@ -16,6 +16,8 @@ pub mod memory;
pub mod objects; pub mod objects;
pub mod traps; pub mod traps;
pub(crate) use objects::thread::user_stack_trace;
/// Loop forever in sleep mode. /// Loop forever in sleep mode.
#[inline] #[inline]
pub fn endless_sleep() -> ! { pub fn endless_sleep() -> ! {

View File

@ -5,5 +5,5 @@
// implemented for x86 and arm // implemented for x86 and arm
trait ASIDControl { trait ASIDControl {
fn make_pool(untyped: Untyped, target_cap_space_cap: CapNodeRootedPath) -> Result<()>; // fn make_pool(untyped: Untyped, target_cap_space_cap: CapNodeRootedPath) -> Result<()>;
} }

View File

@ -5,5 +5,5 @@
// implemented for x86 and arm // implemented for x86 and arm
trait ASIDPool { trait ASIDPool {
fn assign(virt_space: VirtSpace /*Cap*/) -> Result<()>; // fn assign(virt_space: VirtSpace /*Cap*/) -> Result<()>;
} }

View File

@ -3,6 +3,8 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems> * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/ */
use crate::memory::{PhysAddr, VirtAddr};
mod asid_control; mod asid_control;
mod asid_pool; mod asid_pool;
mod page; mod page;
@ -10,6 +12,7 @@ mod page_directory;
mod page_global_directory; mod page_global_directory;
mod page_table; mod page_table;
mod page_upper_directory; mod page_upper_directory;
pub(crate) mod thread;
// Allocation details // Allocation details
@ -40,10 +43,21 @@ mod page_upper_directory;
// * unmap(Page, FrameAllocator)->() // * unmap(Page, FrameAllocator)->()
trait VirtSpace { trait VirtSpace {
fn map(virt_space: VirtSpace/*Cap*/, vaddr: VirtAddr, rights: CapRights, attr: VMAttributes) -> Result<()>; /// ?? fn map_to(
fn unmap() -> Result<()>; /// ?? virt_space: VirtSpace, /*Cap*/
fn remap(virt_space: VirtSpace/*Cap*/, rights: CapRights, attr: VMAttributes) -> Result<()>; /// ?? vaddr: VirtAddr,
fn get_address() -> Result<PhysAddr>;///?? rights: u32, //CapRights,
attr: u32, //VMAttributes,
) -> Result<()>;
/// ??
fn unmap() -> Result<()>; // ??
fn remap(
virt_space: VirtSpace, /*Cap*/
rights: u32, //CapRights,
attr: u32, //VMAttributes,
) -> Result<()>;
/// ??
fn get_address() -> Result<PhysAddr>; //??
} }
// ARM AArch64 processors have a four-level page-table structure, where the // ARM AArch64 processors have a four-level page-table structure, where the
@ -62,7 +76,6 @@ trait VirtSpace {
// +--PageTable (L3) // +--PageTable (L3)
// +--Page<Size4KiB> -- aka Page // +--Page<Size4KiB> -- aka Page
/// Cache data management. /// Cache data management.
trait PageCacheManagement { trait PageCacheManagement {
/// Cleans the data cache out to RAM. /// Cleans the data cache out to RAM.

View File

@ -3,6 +3,11 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems> * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/ */
use crate::arch::{
memory::{PhysAddr, VirtAddr},
objects::PageCacheManagement,
};
struct Page {} struct Page {}
impl Page { impl Page {
@ -11,29 +16,29 @@ impl Page {
fn get_address() -> Result<PhysAddr> { fn get_address() -> Result<PhysAddr> {
todo!() todo!()
} }
fn map( // fn map(
virt_space: VirtSpace, /*Cap*/ // virt_space: VirtSpace, /*Cap*/
vaddr: VirtAddr, // vaddr: VirtAddr,
rights: CapRights, // rights: CapRights,
attr: VMAttributes, // attr: VMAttributes,
) -> Result<()> { // ) -> Result<()> {
todo!() // todo!()
} // }
/// Changes the permissions of an existing mapping. /// Changes the permissions of an existing mapping.
fn remap( // fn remap(
virt_space: VirtSpace, /*Cap*/ // virt_space: VirtSpace, /*Cap*/
rights: CapRights, // rights: CapRights,
attr: VMAttributes, // attr: VMAttributes,
) -> Result<()> { // ) -> Result<()> {
todo!() // todo!()
} // }
fn unmap() -> Result<()> { fn unmap() -> Result<()> {
todo!() todo!()
} }
// MMIO space. // MMIO space.
fn map_io(iospace: IoSpace /*Cap*/, rights: CapRights, ioaddr: VirtAddr) -> Result<()> { // fn map_io(iospace: IoSpace /*Cap*/, rights: CapRights, ioaddr: VirtAddr) -> Result<()> {
todo!() // todo!()
} // }
} }
impl PageCacheManagement for Page { impl PageCacheManagement for Page {

View File

@ -3,6 +3,8 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems> * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/ */
use crate::memory::{mmu::PageUpperDirectory, VirtAddr};
// probably just impl some Mapping trait for these "structs"? // probably just impl some Mapping trait for these "structs"?
// L2 table // L2 table
@ -12,7 +14,7 @@ impl PageDirectory {
fn map( fn map(
pud: PageUpperDirectory, /*Cap*/ pud: PageUpperDirectory, /*Cap*/
vaddr: VirtAddr, vaddr: VirtAddr,
attr: VMAttributes, attr: u32, //VMAttributes,
) -> Result<()> { ) -> Result<()> {
todo!() todo!()
} }

View File

@ -3,6 +3,8 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems> * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/ */
use crate::arch::{memory::PhysAddr, objects::PageCacheManagement};
// L0 table // L0 table
struct PageGlobalDirectory { struct PageGlobalDirectory {
// @todo should also impl VirtSpace to be able to map shit? // @todo should also impl VirtSpace to be able to map shit?

View File

@ -7,9 +7,9 @@
struct PageTable {} struct PageTable {}
impl PageTable { impl PageTable {
fn map(virt_space: VirtSpace /*Cap*/, vaddr: VirtAddr, attr: VMAttributes) -> Result<()> { // fn map_to(virt_space: VirtSpace /*Cap*/, vaddr: VirtAddr, attr: VMAttributes) -> Result<()> {
todo!() // todo!()
} // }
fn unmap() -> Result<()> { fn unmap() -> Result<()> {
todo!() todo!()
} }

View File

@ -3,6 +3,8 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems> * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/ */
use crate::memory::{mmu::PageGlobalDirectory, VirtAddr};
// L1 table // L1 table
struct PageUpperDirectory {} struct PageUpperDirectory {}
@ -10,7 +12,7 @@ impl PageUpperDirectory {
fn map( fn map(
pgd: PageGlobalDirectory, /*Cap*/ pgd: PageGlobalDirectory, /*Cap*/
vaddr: VirtAddr, vaddr: VirtAddr,
attr: VMAttributes, attr: u32, //VMAttributes,
) -> Result<()> { ) -> Result<()> {
todo!() todo!()
} }

View File

@ -0,0 +1,11 @@
//! Arch-specific part of the TCB
struct UserContext {
registers: [u64; 32],
}
pub(crate) struct TCB {
register_context: UserContext,
}
pub(crate) fn user_stack_trace(thread: &TCB) {}

View File

@ -3,8 +3,10 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems> * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/ */
#[cfg(target_arch = "aarch64")] cfg_if::cfg_if! {
if #[cfg(target_arch = "aarch64")] {
#[macro_use] #[macro_use]
pub mod aarch64; pub mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use self::aarch64::*; pub use self::aarch64::*;
}
}

View File

@ -4,6 +4,7 @@
*/ */
use crate::caps::Capability; use crate::caps::Capability;
use snafu::Snafu;
use {super::derivation_tree::DerivationTreeNode, /*crate::memory::PhysAddr,*/ core::fmt}; use {super::derivation_tree::DerivationTreeNode, /*crate::memory::PhysAddr,*/ core::fmt};
// * Capability slots: 16 bytes of memory per slot (exactly one capability). --? // * Capability slots: 16 bytes of memory per slot (exactly one capability). --?
@ -153,7 +154,7 @@ type CapPath = u64;
enum LookupFault { enum LookupFault {
InvalidRoot, InvalidRoot,
GuardMismatch, GuardMismatch,
DepthMismatch(usize, usize), DepthMismatch { expected: usize, actual: usize },
NoResolvedBits, NoResolvedBits,
} }
@ -163,7 +164,7 @@ pub(crate) fn resolve_address_bits(
capptr: CapPath, // CapPtr = u64, aka CapPath capptr: CapPath, // CapPtr = u64, aka CapPath
n_bits: usize, n_bits: usize,
) -> Result<(Slot, BitsRemaining), LookupFault> { ) -> Result<(Slot, BitsRemaining), LookupFault> {
if node_cap.type() != CapNodeCapability { if node_cap.get_type() != CapNodeCapability {
return Err(LookupFault::InvalidRoot); return Err(LookupFault::InvalidRoot);
} }
let mut n_bits = n_bits; let mut n_bits = n_bits;
@ -179,14 +180,17 @@ pub(crate) fn resolve_address_bits(
let cap_guard = node_cap.guard(); let cap_guard = node_cap.guard();
// @todo common code to extract guard_bits from an int? // @todo common code to extract guard_bits from an int?
let guard = (capptr >> std::min(n_bits - guard_bits, 63)) & ((1 << guard_bits) - 1); let guard = (capptr >> core::min(n_bits - guard_bits, 63)) & ((1 << guard_bits) - 1);
if guard_bits > n_bits || guard != cap_guard { if guard_bits > n_bits || guard != cap_guard {
return Err(LookupFault::GuardMismatch); return Err(LookupFault::GuardMismatch);
} }
if level_bits > n_bits { if level_bits > n_bits {
return Err(LookupFault::DepthMismatch(level_bits, n_bits)); return Err(LookupFault::DepthMismatch {
expected: level_bits,
actual: n_bits,
});
} }
let offset = (capptr >> (n_bits - level_bits)) & ((1 << radix_bits) - 1); let offset = (capptr >> (n_bits - level_bits)) & ((1 << radix_bits) - 1);
@ -200,7 +204,7 @@ pub(crate) fn resolve_address_bits(
n_bits -= level_bits; n_bits -= level_bits;
node_cap = slot.capability; node_cap = slot.capability;
if node_cap.type() != CapNodeCapability { if node_cap.get_type() != CapNodeCapability {
return Ok((slot, n_bits)); return Ok((slot, n_bits));
} }
} }

View File

@ -34,13 +34,16 @@ mod endpoint_cap;
mod irq_control_cap; mod irq_control_cap;
mod irq_handler_cap; mod irq_handler_cap;
mod notification_cap; mod notification_cap;
mod null_cap; pub mod null_cap;
mod reply_cap; pub mod reply_cap;
mod resume_cap; mod resume_cap;
mod thread_cap; mod thread_cap;
mod untyped_cap; mod untyped_cap;
mod zombie_cap; mod zombie_cap;
pub use null_cap::NullCapability;
pub use reply_cap::ReplyCapability;
/// Opaque capability object, manipulated by the kernel. /// Opaque capability object, manipulated by the kernel.
pub trait Capability { pub trait Capability {
/// ///
@ -70,6 +73,9 @@ macro_rules! capdef {
paste! { paste! {
#[doc = "Wrapper representing `" $name "Capability`."] #[doc = "Wrapper representing `" $name "Capability`."]
pub struct [<$name Capability>](LocalRegisterCopy<u128, [<$name Cap>]::Register>); pub struct [<$name Capability>](LocalRegisterCopy<u128, [<$name Cap>]::Register>);
impl [<$name Capability>] {
type Type = [<$name Cap>]::Register;
}
impl Capability for [<$name Capability>] { impl Capability for [<$name Capability>] {
#[inline] #[inline]
fn as_u128(&self) -> u128 { fn as_u128(&self) -> u128 {

View File

@ -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 api;
mod caps; mod caps;
mod devices; mod devices;
mod macros; mod macros;
@ -363,4 +364,9 @@ mod main_tests {
fn test_data_abort_trap() { fn test_data_abort_trap() {
check_data_abort_trap() check_data_abort_trap()
} }
#[test_case]
fn test_user_thread_syscall() {
// To test syscall from user-space we need to construct a user-space thread and switch to it
}
} }

View File

@ -16,7 +16,8 @@
// * _Capability spaces_ store capabilities (i.e., access rights) to kernel services along with // * _Capability spaces_ store capabilities (i.e., access rights) to kernel services along with
// their book-keeping information. // their book-keeping information.
pub mod kernel_object; pub mod nucleus_object;
pub mod thread;
pub mod untyped; pub mod untyped;
pub use kernel_object::KernelObject; pub(crate) use nucleus_object::NucleusObject;

View File

@ -3,7 +3,7 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems> * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/ */
pub(crate) trait KernelObject { pub(crate) trait NucleusObject {
fn size_bits() -> usize; fn size_bits() -> usize;
fn invoke(); fn invoke();
} }

View File

@ -3,51 +3,135 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems> * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/ */
trait Thread { use crate::{arch, arch::memory::VirtAddr};
// Configuration
// Effectively, SetSpace followed by SetIPCBuffer.
fn configure(fault_endpoint: Cap, cap_space_root: Cap, cap_space_root_data: CapNodeConfig, virt_space_root: Cap, virt_space_root_data: (), ipc_buffer_frame: Cap, ipc_buffer_offset: usize) -> Result<()>;
fn set_space(fault_endpoint: Cap, cap_space_root: Cap, cap_space_root_data: CapNodeConfig, virt_space_root: Cap, virt_space_root_data: ()) -> Result<()>;
fn set_ipc_buffer(ipc_buffer_frame: CapNode, ipc_buffer_offset: usize) -> Result<()>;
// Debugging tools
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<()>;
// Scheduling
fn suspend() -> Result<()>;
fn resume() -> 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<()>;
// TCB configuration
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<()>;
// Notifications
fn bind_notification(notification: CapNode) -> Result<()>;
fn unbind_notification() -> Result<()>;
// Arch-specific // trait Thread {
// fn set_tls_base(tls_base: usize) -> Result<()>; // // Configuration
// virtualized - x86-specific // // Effectively, SetSpace followed by SetIPCBuffer.
// fn set_ept_root(eptpml: X86::EPTPML4) -> Result<()>; // fn configure(fault_endpoint: Cap, cap_space_root: Cap, cap_space_root_data: CapNodeConfig, virt_space_root: Cap, virt_space_root_data: (), ipc_buffer_frame: Cap, ipc_buffer_offset: usize) -> Result<()>;
} // fn set_space(fault_endpoint: Cap, cap_space_root: Cap, cap_space_root_data: CapNodeConfig, virt_space_root: Cap, virt_space_root_data: ()) -> Result<()>;
// fn set_ipc_buffer(ipc_buffer_frame: CapNode, ipc_buffer_offset: usize) -> Result<()>;
// @todo <<SchedContext>> // // Debugging tools
// fn configure_single_stepping(bp_num: u16, num_insns): Result<SingleStepping>;
// struct Thread {} // fn get_breakpoint(bp_num: u16) -> Result<BreakpointInfo>;
struct TCB { // fn set_breakpoint(bp_num: u16, bp: BreakpointInfo) -> Result<()>;
capability: u128, // should actually be a CapPath here - this is the argument to // fn unset_breakpoint(bp_num: u16) -> Result<()>;
// Thread.read_registers(cap, ... call for example. // // Scheduling
} // fn suspend() -> Result<()>;
// fn resume() -> Result<()>;
impl Thread for TCB { // 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<()>;
// // TCB configuration
// 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<()>;
// // Notifications
// fn bind_notification(notification: CapNode) -> Result<()>;
// fn unbind_notification() -> 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 Thread {}
// struct TCB {
// capability: u128, // should actually be a CapPath here - this is the argument to
// // Thread.read_registers(cap, ... call for example.
// }
//
// impl Thread for TCB {
// // ...
// }
// impl super::KernelObject for Thread {} // impl super::KernelObject for Thread {}
impl super::KernelObject for TCB { impl super::KernelObject for TCB {
const SIZE_BITS: usize = 12; const SIZE_BITS: usize = 12;
} }
// -- from actual code parts in api.rs
/* TCB: size 64 bytes + sizeof(arch_tcb_t) (aligned to nearest power of 2) */
struct TCB {
arch_specific: arch::objects::TCB,
state: ThreadState, // 12 bytes?
/* Notification that this TCB is bound to. If this is set, when this TCB waits on
* any sync endpoint, it may receive a signal from a Notification object.
* 4 bytes*/
// notification_t *tcbBoundNotification;
fault: Fault, // 8 bytes?
lookup_failure: LookupFault, // 8 bytes
/* Domain, 1 byte (packed to 4) */
domain: Domain,
/* maximum controlled priorioty, 1 byte (packed to 4) */
mcp: Priority,
/* Priority, 1 byte (packed to 4) */
priority: Priority,
/* Timeslice remaining, 4 bytes */
time_slice: u32,
/* Capability pointer to thread fault handler, 8 bytes */
fault_handler: CapPath,
/* userland virtual address of thread IPC buffer, 8 bytes */
ipc_buffer: VirtAddr,
/* Previous and next pointers for scheduler queues , 8+8 bytes */
sched_next: *mut TCB,
sched_prev: *mut TCB,
/* Previous and next pointers for endpoint and notification queues, 8+8 bytes */
ep_next: *mut TCB,
ep_prev: *mut TCB,
/* Use any remaining space for a thread name */
name: &str,
// name_storage: [u8],// add SIZE_BITS calculations for length of storage in here somewhere
}
pub(crate) enum ThreadState {
Inactive,
Restart,
}
impl TCB {
fn get_register(&self, register_index: usize) {
self.arch_tcb.register_context.registers[register_index]
}
fn lookup_cap_and_slot() -> Result<()> {}
fn get_restart_pc() {}
fn lookup_ipc_buffer(some: bool) {}
fn lookup_extra_caps() -> Result<()> {}
fn get_state() -> ThreadState {}
fn set_state(state: ThreadState) {}
fn get_caller_slot() -> Slot {}
fn send_fault_ipc(&self) {}
fn replyFromKernel_success_empty() {}
fn replyFromKernel_error() {}
// // Configuration
// // Effectively, SetSpace followed by SetIPCBuffer.
// fn configure(fault_endpoint: Cap, cap_space_root: Cap, cap_space_root_data: CapNodeConfig, virt_space_root: Cap, virt_space_root_data: (), ipc_buffer_frame: Cap, ipc_buffer_offset: usize) -> Result<()>;
// fn set_space(fault_endpoint: Cap, cap_space_root: Cap, cap_space_root_data: CapNodeConfig, virt_space_root: Cap, virt_space_root_data: ()) -> Result<()>;
// fn set_ipc_buffer(ipc_buffer_frame: CapNode, ipc_buffer_offset: usize) -> Result<()>;
// // Debugging tools
// 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<()>;
// // Scheduling
// fn suspend() -> Result<()>;
// fn resume() -> 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<()>;
// // TCB configuration
// 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<()>;
// // Notifications
// fn bind_notification(notification: CapNode) -> Result<()>;
// fn unbind_notification() -> Result<()>;
}

View File

@ -3,9 +3,17 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems> * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/ */
use snafu::Snafu;
// 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.
// Untyped.retype() derives cap to a typed cap (derivation tree must be maintained)
pub(crate) struct Untyped {} pub(crate) struct Untyped {}
impl super::KernelObject for Untyped { impl super::NucleusObject for Untyped {
fn size_bits() -> usize { fn size_bits() -> usize {
unimplemented!() unimplemented!()
} }
@ -15,8 +23,22 @@ impl super::KernelObject for Untyped {
} }
} }
#[derive(Debug, Snafu)]
enum RetypeError {
Whatever,
}
impl Untyped { impl Untyped {
fn retype() {} // Uses T::SIZE_BITS to properly size the resulting object
// in some cases size_bits must be passed as argument though...
// @todo return an array of caps?
fn retype<T: NucleusObject>(
target_cap: CapNodeRootedPath,
target_cap_offset: usize,
num_objects: usize,
) -> Result<CapSlice, RetypeError> {
Err(RetypeError::Whatever)
}
} }
enum MemoryKind { enum MemoryKind {
@ -24,21 +46,5 @@ enum MemoryKind {
Device, 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.
// Untyped.retype() derives cap to a typed cap (derivation tree must be maintained)
trait Untyped {
// Uses T::SIZE_BITS to properly size the resulting object
// in some cases size_bits must be passed as argument though...
fn retype<T: NucleusObject>(
target_cap: CapNodeRootedPath,
target_cap_offset: usize,
num_objects: usize,
) -> Result<CapSlice>; // @todo return an array of caps?
}
// with GATs // with GATs
// trait Retyped { type Result = CapTable::<T> .. } // trait Retyped { type Result = CapTable::<T> .. }

View File

@ -1,3 +1,3 @@
pub fn syscall(number: u64) { pub fn syscall(_number: u64) {
asm!("svc #1234") unsafe { asm!("svc #1234") }
} }

View File

@ -1,7 +1,13 @@
#![no_std]
#![feature(asm)]
pub mod arch; pub mod arch;
pub use arch::syscall; pub use arch::syscall;
// @todo make this use numeric constants for ABI compat
// but to keep this interface simpler, enum-to-numeric remapping will be implemented inside of the
// syscall() fn.
pub enum SysCall { pub enum SysCall {
Send, Send,
NBSend, NBSend,
@ -11,6 +17,16 @@ pub enum SysCall {
ReplyRecv, ReplyRecv,
NBRecv, NBRecv,
Yield, Yield,
#[cfg(debug)]
DebugPutChar,
#[cfg(debug)]
DebugHalt,
#[cfg(debug)]
DebugSnapshot,
#[cfg(debug)]
DebugCapIdentify,
#[cfg(debug)]
DebugNameThread,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -0,0 +1,46 @@
// This is probably what a user-space look of the capabilities should look like? Like direct access
// to some kernel objects via possible invocations.
// Some of these functions are probably kernel-space only and so should be removed from user-side interface.
trait Thread {
// Configuration
// Effectively, SetSpace followed by SetIPCBuffer.
fn configure(fault_endpoint: Cap, cap_space_root: Cap, cap_space_root_data: CapNodeConfig, virt_space_root: Cap, virt_space_root_data: (), ipc_buffer_frame: Cap, ipc_buffer_offset: usize) -> Result<()>;
fn set_space(fault_endpoint: Cap, cap_space_root: Cap, cap_space_root_data: CapNodeConfig, virt_space_root: Cap, virt_space_root_data: ()) -> Result<()>;
fn set_ipc_buffer(ipc_buffer_frame: CapNode, ipc_buffer_offset: usize) -> Result<()>;
// Debugging tools
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<()>;
// Scheduling
fn suspend() -> Result<()>;
fn resume() -> 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<()>;
// TCB configuration
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<()>;
// Notifications
fn bind_notification(notification: CapNode) -> Result<()>;
fn unbind_notification() -> 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 Thread {}
struct TCB {
capability: u128, // should actually be a CapPath here - this is the argument to
// Thread.read_registers(cap, ... call for example.
}
impl Thread for TCB {
// ...
}