diff --git a/nucleus/src/api.rs b/nucleus/src/api.rs new file mode 100644 index 0000000..fa3d3c2 --- /dev/null +++ b/nucleus/src/api.rs @@ -0,0 +1,82 @@ +// 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 Nucleus {} + +impl API for Nucleus { + fn send(cap: _, msg_info: _) { + unimplemented!() + } + + fn recv(src: _, reply: _) -> _ { + unimplemented!() + } + + fn call(cap: _, msg_info: _) -> _ { + unimplemented!() + } + + fn reply(msg_info: _) { + unimplemented!() + } + + fn nb_send(dest: _, msg_info: _) { + unimplemented!() + } + + fn reply_recv(src: _, reply: _, msg_info: _) -> _ { + unimplemented!() + } + + fn nb_send_recv(dest: _, msg_info: _, src: _, reply: _) -> _ { + unimplemented!() + } + + fn nb_recv(src: _) -> _ { + unimplemented!() + } + + fn nb_send_wait(cap: _, msg_info: _, src: _) -> _ { + unimplemented!() + } + + fn wait(src: _) -> _ { + unimplemented!() + } + + fn r#yield() { + unimplemented!() + } +} diff --git a/nucleus/src/arch/aarch64/mod.rs b/nucleus/src/arch/aarch64/mod.rs index ede7a08..f090d37 100644 --- a/nucleus/src/arch/aarch64/mod.rs +++ b/nucleus/src/arch/aarch64/mod.rs @@ -13,6 +13,7 @@ pub use self::caps::*; #[cfg(feature = "jtag")] pub mod jtag; pub mod memory; +pub mod objects; pub mod traps; /// Loop forever in sleep mode. diff --git a/nucleus/src/arch/aarch64/objects.rs b/nucleus/src/arch/aarch64/objects.rs deleted file mode 100644 index ef8134f..0000000 --- a/nucleus/src/arch/aarch64/objects.rs +++ /dev/null @@ -1,465 +0,0 @@ -/* - * 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. - -// 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(target_cap: CapNodeRootedPath, target_cap_offset: usize, num_objects: usize) -> Result; // @todo return an array of caps? -} - -// with GATs -// trait Retyped { type Result = CapTable:: .. } - -// 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;///?? -} - -// 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 -- aka HugePage -// | or -// +--PageDirectory (L2) -// +--Page -- aka LargePage -// | or -// +--PageTable (L3) -// +--Page -- aka Page - - -/// Cache data management. -trait PageCacheManagement { - /// 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 -// mod aarch64 { - -struct Page {} - -impl Page { - // VirtSpace-like interface. - /// Get the physical address of the underlying frame. - fn get_address() -> Result { todo!() } - fn map(virt_space: VirtSpace/*Cap*/, vaddr: VirtAddr, rights: CapRights, attr: VMAttributes) -> Result<()> { todo!() } - /// Changes the permissions of an existing mapping. - fn remap(virt_space: VirtSpace/*Cap*/, rights: CapRights, attr: VMAttributes) -> Result<()> { todo!() } - fn unmap() -> Result<()> { todo!() } - // MMIO space. - fn map_io(iospace: IoSpace/*Cap*/, rights: CapRights, ioaddr: VirtAddr) -> Result<()> { todo!() } -} - -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<()> { todo!() } - fn unmap() -> Result<()> { todo!() } -} - -// 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<()> { todo!() } - fn unmap() -> Result<()> { todo!() } -} - -// L1 table -struct PageUpperDirectory {} - -impl PageUpperDirectory { - fn map(pgd: PageGlobalDirectory/*Cap*/, vaddr: VirtAddr, attr: VMAttributes) -> Result<()> { todo!() } - fn unmap() -> Result<()> { todo!() } -} - -// 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... from FrameCaps - - -// 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; - fn get_breakpoint(bp_num: u16) -> Result; - 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 <> - -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 { - fn configure(fault_endpoint: _, cap_space_root: _, cap_space_root_data: _, virt_space_root: _, ipc_buffer_frame: _, ipc_buffer_offset: usize) -> _ { - unimplemented!() - } - - fn set_space(fault_endpoint: _, cap_space_root: _, cap_space_root_data: _, virt_space_root: _) -> _ { - unimplemented!() - } - - fn configure_single_stepping(bp_num: u16, _: _) { - unimplemented!() - } - - fn get_breakpoint(bp_num: u16) -> _ { - unimplemented!() - } - - fn set_breakpoint(bp_num: u16, bp: _) -> _ { - unimplemented!() - } - - fn unset_breakpoint(bp_num: u16) -> _ { - unimplemented!() - } - - fn suspend() -> _ { - unimplemented!() - } - - fn resume() -> _ { - unimplemented!() - } - - fn copy_registers(source: TCB, suspend_source: bool, resume_target: bool, transfer_frame_regs: bool, transfer_integer_regs: bool, arch_flags: u8) -> _ { - unimplemented!() - } - - fn read_registers(suspend_source: bool, arch_flags: u8, num_regs: u16, register_context: &mut _) -> _ { - unimplemented!() - } - - fn write_registers(resume_target: bool, arch_flags: u8, num_regs: u16, register_context: &_) -> _ { - unimplemented!() - } - - fn bind_notification(notification: _) -> _ { - unimplemented!() - } - - fn unbind_notification() -> _ { - unimplemented!() - } - - fn set_priority(authority: TCB, priority: u32) -> _ { - unimplemented!() - } - - fn set_mc_priority(authority: TCB, mcp: u32) -> _ { - unimplemented!() - } - - fn set_sched_params(authority: TCB, mcp: u32, priority: u32) -> _ { - unimplemented!() - } - - fn set_affinity(affinity: u64) -> _ { - unimplemented!() - } - - fn set_ipc_buffer(ipc_buffer_frame: _, ipc_buffer_offset: usize) -> _ { - unimplemented!() - } - - fn set_tls_base(tls_base: usize) -> _ { - unimplemented!() - } - - fn set_ept_root(eptpml: _) -> _ { - unimplemented!() - } -} -impl KernelObject for TCB { - const SIZE_BITS: usize = 12; -} - -trait Notification { - fn signal(dest: Cap); - fn wait(src: Cap) -> Result>; - 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 Nucleus {} - -impl API for Nucleus { - fn send(cap: _, msg_info: _) { - unimplemented!() - } - - fn recv(src: _, reply: _) -> _ { - unimplemented!() - } - - fn call(cap: _, msg_info: _) -> _ { - unimplemented!() - } - - fn reply(msg_info: _) { - unimplemented!() - } - - fn nb_send(dest: _, msg_info: _) { - unimplemented!() - } - - fn reply_recv(src: _, reply: _, msg_info: _) -> _ { - unimplemented!() - } - - fn nb_send_recv(dest: _, msg_info: _, src: _, reply: _) -> _ { - unimplemented!() - } - - fn nb_recv(src: _) -> _ { - unimplemented!() - } - - fn nb_send_wait(cap: _, msg_info: _, src: _) -> _ { - unimplemented!() - } - - fn wait(src: _) -> _ { - unimplemented!() - } - - fn r#yield() { - unimplemented!() - } -} - -trait DomainSet { - // ?? - fn set(domain: Dom, thread: TCB); -} - -// Virtualisation -// ARM -trait VCPU { - fn inject_irq(virq: u16, priority: u8, group: u8, index: u8) -> Result<()>; - fn read_registers(); - fn write_registers(); - fn set_tcb(); -} diff --git a/nucleus/src/arch/aarch64/objects/asid_control.rs b/nucleus/src/arch/aarch64/objects/asid_control.rs new file mode 100644 index 0000000..305d2f4 --- /dev/null +++ b/nucleus/src/arch/aarch64/objects/asid_control.rs @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +// implemented for x86 and arm +trait ASIDControl { + fn make_pool(untyped: Untyped, target_cap_space_cap: CapNodeRootedPath) -> Result<()>; +} diff --git a/nucleus/src/arch/aarch64/objects/asid_pool.rs b/nucleus/src/arch/aarch64/objects/asid_pool.rs new file mode 100644 index 0000000..690a17c --- /dev/null +++ b/nucleus/src/arch/aarch64/objects/asid_pool.rs @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +// implemented for x86 and arm +trait ASIDPool { + fn assign(virt_space: VirtSpace /*Cap*/) -> Result<()>; +} diff --git a/nucleus/src/arch/aarch64/objects/mod.rs b/nucleus/src/arch/aarch64/objects/mod.rs new file mode 100644 index 0000000..9fd62ce --- /dev/null +++ b/nucleus/src/arch/aarch64/objects/mod.rs @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +mod asid_control; +mod asid_pool; +mod page; +mod page_directory; +mod page_global_directory; +mod page_table; +mod page_upper_directory; + +// 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... from FrameCaps + +// 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. + +//============================================================================= + +// 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;///?? +} + +// 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 -- aka HugePage +// | or +// +--PageDirectory (L2) +// +--Page -- aka LargePage +// | or +// +--PageTable (L3) +// +--Page -- aka Page + + +/// Cache data management. +trait PageCacheManagement { + /// 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<()>; +} diff --git a/nucleus/src/arch/aarch64/objects/page.rs b/nucleus/src/arch/aarch64/objects/page.rs new file mode 100644 index 0000000..9c878c4 --- /dev/null +++ b/nucleus/src/arch/aarch64/objects/page.rs @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +struct Page {} + +impl Page { + // VirtSpace-like interface. + /// Get the physical address of the underlying frame. + fn get_address() -> Result { + todo!() + } + fn map( + virt_space: VirtSpace, /*Cap*/ + vaddr: VirtAddr, + rights: CapRights, + attr: VMAttributes, + ) -> Result<()> { + todo!() + } + /// Changes the permissions of an existing mapping. + fn remap( + virt_space: VirtSpace, /*Cap*/ + rights: CapRights, + attr: VMAttributes, + ) -> Result<()> { + todo!() + } + fn unmap() -> Result<()> { + todo!() + } + // MMIO space. + fn map_io(iospace: IoSpace /*Cap*/, rights: CapRights, ioaddr: VirtAddr) -> Result<()> { + todo!() + } +} + +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!() + } +} diff --git a/nucleus/src/arch/aarch64/objects/page_directory.rs b/nucleus/src/arch/aarch64/objects/page_directory.rs new file mode 100644 index 0000000..e916a9e --- /dev/null +++ b/nucleus/src/arch/aarch64/objects/page_directory.rs @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +// 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<()> { + todo!() + } + fn unmap() -> Result<()> { + todo!() + } +} diff --git a/nucleus/src/arch/aarch64/objects/page_global_directory.rs b/nucleus/src/arch/aarch64/objects/page_global_directory.rs new file mode 100644 index 0000000..ed87933 --- /dev/null +++ b/nucleus/src/arch/aarch64/objects/page_global_directory.rs @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +// 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!() + } +} diff --git a/nucleus/src/arch/aarch64/objects/page_table.rs b/nucleus/src/arch/aarch64/objects/page_table.rs new file mode 100644 index 0000000..947d80b --- /dev/null +++ b/nucleus/src/arch/aarch64/objects/page_table.rs @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +// L3 tables +struct PageTable {} + +impl PageTable { + fn map(virt_space: VirtSpace /*Cap*/, vaddr: VirtAddr, attr: VMAttributes) -> Result<()> { + todo!() + } + fn unmap() -> Result<()> { + todo!() + } +} diff --git a/nucleus/src/arch/aarch64/objects/page_upper_directory.rs b/nucleus/src/arch/aarch64/objects/page_upper_directory.rs new file mode 100644 index 0000000..18a38f4 --- /dev/null +++ b/nucleus/src/arch/aarch64/objects/page_upper_directory.rs @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +// L1 table +struct PageUpperDirectory {} + +impl PageUpperDirectory { + fn map( + pgd: PageGlobalDirectory, /*Cap*/ + vaddr: VirtAddr, + attr: VMAttributes, + ) -> Result<()> { + todo!() + } + fn unmap() -> Result<()> { + todo!() + } +} diff --git a/nucleus/src/main.rs b/nucleus/src/main.rs index e380cdb..4bee691 100644 --- a/nucleus/src/main.rs +++ b/nucleus/src/main.rs @@ -40,6 +40,7 @@ mod caps; mod devices; mod macros; mod mm; +mod objects; mod panic; mod platform; #[cfg(feature = "qemu")] diff --git a/nucleus/src/objects/capnode.rs b/nucleus/src/objects/capnode.rs new file mode 100644 index 0000000..1c98a93 --- /dev/null +++ b/nucleus/src/objects/capnode.rs @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +struct CapNode {} + +impl super::KernelObject for CapNode { + fn size_bits() -> usize { + unimplemented!() + } + + fn invoke() { + unimplemented!() + } +} diff --git a/nucleus/src/objects/endpoint.rs b/nucleus/src/objects/endpoint.rs new file mode 100644 index 0000000..c3615b7 --- /dev/null +++ b/nucleus/src/objects/endpoint.rs @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +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, + ], + ], +} diff --git a/nucleus/src/objects/irq_control.rs b/nucleus/src/objects/irq_control.rs new file mode 100644 index 0000000..2d67f52 --- /dev/null +++ b/nucleus/src/objects/irq_control.rs @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +trait IRQControl { + fn get(irq: u32, dest: CapNodeRootedPath) -> Result<()>; + // ARM specific? + fn get_trigger(); + fn get_trigger_core(); +} diff --git a/nucleus/src/objects/irq_handler.rs b/nucleus/src/objects/irq_handler.rs new file mode 100644 index 0000000..2e75a58 --- /dev/null +++ b/nucleus/src/objects/irq_handler.rs @@ -0,0 +1,9 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +trait IRQHandler { + fn set_notification(notification: CapNode) -> Result<()>; + fn ack() -> Result<()>; + fn clear() -> Result<()>; +} diff --git a/nucleus/src/objects/kernel_object.rs b/nucleus/src/objects/kernel_object.rs new file mode 100644 index 0000000..b1cab59 --- /dev/null +++ b/nucleus/src/objects/kernel_object.rs @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +pub(crate) trait KernelObject { + fn size_bits() -> usize; + fn invoke(); +} diff --git a/nucleus/src/objects/mod.rs b/nucleus/src/objects/mod.rs new file mode 100644 index 0000000..f7d4385 --- /dev/null +++ b/nucleus/src/objects/mod.rs @@ -0,0 +1,21 @@ +/* + * 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. + +pub mod kernel_object; +pub mod untyped; + +pub use kernel_object::KernelObject; diff --git a/nucleus/src/objects/notification.rs b/nucleus/src/objects/notification.rs new file mode 100644 index 0000000..89a539f --- /dev/null +++ b/nucleus/src/objects/notification.rs @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +// @todo replace with Event +register_bitfields! { + u128, + Notification [ + BoundTCB OFFSET(16) NUMBITS(48) [], + MsgId OFFSET(64) NUMBITS(64) [], + ], + NotificationQueue [ + QueueHead OFFSET(16) NUMBITS(48) [], + QueueTail OFFSET(64) NUMBITS(48) [], + State OFFSET(126) NUMBITS(2) [ + Idle = 00b, + Waiting = 01b, + Active = 10b, + ], + ] +} + +trait Notification { + fn signal(dest: Cap); + fn wait(src: Cap) -> Result>; + fn poll(cap: Cap) -> Result<(MessageInfo, Option<&Badge>)>; +} diff --git a/nucleus/src/objects/thread.rs b/nucleus/src/objects/thread.rs new file mode 100644 index 0000000..720889e --- /dev/null +++ b/nucleus/src/objects/thread.rs @@ -0,0 +1,127 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +struct Thread {} + +impl super::KernelObject for Thread {} + + +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; + fn get_breakpoint(bp_num: u16) -> Result; + 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 <> + +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 { + fn configure(fault_endpoint: _, cap_space_root: _, cap_space_root_data: _, virt_space_root: _, ipc_buffer_frame: _, ipc_buffer_offset: usize) -> _ { + unimplemented!() + } + + fn set_space(fault_endpoint: _, cap_space_root: _, cap_space_root_data: _, virt_space_root: _) -> _ { + unimplemented!() + } + + fn configure_single_stepping(bp_num: u16, _: _) { + unimplemented!() + } + + fn get_breakpoint(bp_num: u16) -> _ { + unimplemented!() + } + + fn set_breakpoint(bp_num: u16, bp: _) -> _ { + unimplemented!() + } + + fn unset_breakpoint(bp_num: u16) -> _ { + unimplemented!() + } + + fn suspend() -> _ { + unimplemented!() + } + + fn resume() -> _ { + unimplemented!() + } + + fn copy_registers(source: TCB, suspend_source: bool, resume_target: bool, transfer_frame_regs: bool, transfer_integer_regs: bool, arch_flags: u8) -> _ { + unimplemented!() + } + + fn read_registers(suspend_source: bool, arch_flags: u8, num_regs: u16, register_context: &mut _) -> _ { + unimplemented!() + } + + fn write_registers(resume_target: bool, arch_flags: u8, num_regs: u16, register_context: &_) -> _ { + unimplemented!() + } + + fn bind_notification(notification: _) -> _ { + unimplemented!() + } + + fn unbind_notification() -> _ { + unimplemented!() + } + + fn set_priority(authority: TCB, priority: u32) -> _ { + unimplemented!() + } + + fn set_mc_priority(authority: TCB, mcp: u32) -> _ { + unimplemented!() + } + + fn set_sched_params(authority: TCB, mcp: u32, priority: u32) -> _ { + unimplemented!() + } + + fn set_affinity(affinity: u64) -> _ { + unimplemented!() + } + + fn set_ipc_buffer(ipc_buffer_frame: _, ipc_buffer_offset: usize) -> _ { + unimplemented!() + } + + fn set_tls_base(tls_base: usize) -> _ { + unimplemented!() + } + + fn set_ept_root(eptpml: _) -> _ { + unimplemented!() + } +} + +impl KernelObject for TCB { + const SIZE_BITS: usize = 12; +} diff --git a/nucleus/src/objects/untyped.rs b/nucleus/src/objects/untyped.rs new file mode 100644 index 0000000..cab70c4 --- /dev/null +++ b/nucleus/src/objects/untyped.rs @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +pub(crate) struct Untyped {} + +impl super::KernelObject for Untyped { + fn size_bits() -> usize { + unimplemented!() + } + + fn invoke() { + unimplemented!() + } +} + +impl Untyped { + fn retype() {} +} + +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. + +// 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( + target_cap: CapNodeRootedPath, + target_cap_offset: usize, + num_objects: usize, + ) -> Result; // @todo return an array of caps? +} + +// with GATs +// trait Retyped { type Result = CapTable:: .. }