diff --git a/Cargo.lock b/Cargo.lock index 3d8212e67..9c4b5a182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2743,6 +2743,28 @@ dependencies = [ "managed", ] +[[package]] +name = "spawn-task" +version = "0.1.0" +dependencies = [ + "object", + "sel4", + "sel4-root-task", +] + +[[package]] +name = "spawn-task-child" +version = "0.1.0" +dependencies = [ + "cfg-if", + "sel4", + "sel4-dlmalloc", + "sel4-panicking", + "sel4-panicking-env", + "sel4-runtime-common", + "sel4-sync", +] + [[package]] name = "spawn-thread" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 66d0864e3..c13d696b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,8 @@ members = [ "crates/examples/root-task/example-root-task", "crates/examples/root-task/example-root-task-without-runtime", "crates/examples/root-task/hello", + "crates/examples/root-task/spawn-task", + "crates/examples/root-task/spawn-task/child", "crates/examples/root-task/spawn-thread", "crates/private/meta", "crates/private/support/sel4-simple-task/config-types", diff --git a/crates/examples/root-task/spawn-task/Cargo.nix b/crates/examples/root-task/spawn-task/Cargo.nix new file mode 100644 index 000000000..7b1c88a9c --- /dev/null +++ b/crates/examples/root-task/spawn-task/Cargo.nix @@ -0,0 +1,18 @@ +# +# Copyright 2024, Colias Group, LLC +# +# SPDX-License-Identifier: BSD-2-Clause +# + +{ mk, versions, localCrates }: + +mk { + package.name = "spawn-task"; + dependencies = { + object = { version = versions.object; default-features = false; features = [ "read" ]; }; + inherit (localCrates) + sel4 + sel4-root-task + ; + }; +} diff --git a/crates/examples/root-task/spawn-task/Cargo.toml b/crates/examples/root-task/spawn-task/Cargo.toml new file mode 100644 index 000000000..0aec577c0 --- /dev/null +++ b/crates/examples/root-task/spawn-task/Cargo.toml @@ -0,0 +1,22 @@ +# +# Copyright 2023, Colias Group, LLC +# +# SPDX-License-Identifier: BSD-2-Clause +# +# +# This file is generated from './Cargo.nix'. You can edit this file directly +# if you are not using this project's Cargo manifest management tools. +# See 'hacking/cargo-manifest-management/README.md' for more information. +# + +[package] +name = "spawn-task" +version = "0.1.0" +authors = ["Nick Spinale "] +edition = "2021" +license = "BSD-2-Clause" + +[dependencies] +object = { version = "0.32.1", default-features = false, features = ["read"] } +sel4 = { path = "../../../sel4" } +sel4-root-task = { path = "../../../sel4-root-task" } diff --git a/crates/examples/root-task/spawn-task/child/Cargo.nix b/crates/examples/root-task/spawn-task/child/Cargo.nix new file mode 100644 index 000000000..ce1e8c07a --- /dev/null +++ b/crates/examples/root-task/spawn-task/child/Cargo.nix @@ -0,0 +1,26 @@ +# +# Copyright 2024, Colias Group, LLC +# +# SPDX-License-Identifier: BSD-2-Clause +# + +{ mk, versions, localCrates }: + +mk { + package.name = "spawn-task-child"; + dependencies = { + inherit (versions) cfg-if; + inherit (localCrates) + sel4 + sel4-panicking-env + sel4-dlmalloc + sel4-sync + ; + sel4-panicking = localCrates.sel4-panicking // { + features = [ "unwinding" "alloc" ]; + }; + sel4-runtime-common = localCrates.sel4-runtime-common // { + features = [ "start" "tls" "unwinding" ]; + }; + }; +} diff --git a/crates/examples/root-task/spawn-task/child/Cargo.toml b/crates/examples/root-task/spawn-task/child/Cargo.toml new file mode 100644 index 000000000..c9b81e7c6 --- /dev/null +++ b/crates/examples/root-task/spawn-task/child/Cargo.toml @@ -0,0 +1,29 @@ +# +# Copyright 2023, Colias Group, LLC +# +# SPDX-License-Identifier: BSD-2-Clause +# +# +# This file is generated from './Cargo.nix'. You can edit this file directly +# if you are not using this project's Cargo manifest management tools. +# See 'hacking/cargo-manifest-management/README.md' for more information. +# + +[package] +name = "spawn-task-child" +version = "0.1.0" +authors = ["Nick Spinale "] +edition = "2021" +license = "BSD-2-Clause" + +[dependencies] +cfg-if = "1.0.0" +sel4 = { path = "../../../../sel4" } +sel4-dlmalloc = { path = "../../../../sel4-dlmalloc" } +sel4-panicking = { path = "../../../../sel4-panicking", features = ["unwinding", "alloc"] } +sel4-panicking-env = { path = "../../../../sel4-panicking/env" } +sel4-sync = { path = "../../../../sel4-sync" } + +[dependencies.sel4-runtime-common] +path = "../../../../sel4-runtime-common" +features = ["start", "tls", "unwinding"] diff --git a/crates/examples/root-task/spawn-task/child/src/main.rs b/crates/examples/root-task/spawn-task/child/src/main.rs new file mode 100644 index 000000000..14b4eef2e --- /dev/null +++ b/crates/examples/root-task/spawn-task/child/src/main.rs @@ -0,0 +1,20 @@ +// +// Copyright 2024, Colias Group, LLC +// +// SPDX-License-Identifier: BSD-2-Clause +// + +#![no_std] +#![no_main] + +mod runtime; + +fn main() -> ! { + sel4::debug_println!("In child task"); + + sel4::Notification::from_bits(1).signal(); + + sel4::Tcb::from_bits(2).tcb_suspend().unwrap(); + + unreachable!() +} diff --git a/crates/examples/root-task/spawn-task/child/src/runtime.rs b/crates/examples/root-task/spawn-task/child/src/runtime.rs new file mode 100644 index 000000000..aacc57a29 --- /dev/null +++ b/crates/examples/root-task/spawn-task/child/src/runtime.rs @@ -0,0 +1,67 @@ +// +// Copyright 2024, Colias Group, LLC +// +// SPDX-License-Identifier: BSD-2-Clause +// + +use core::ptr; + +use sel4::CapTypeForFrameObjectOfFixedSize; +use sel4_dlmalloc::{StaticDlmallocGlobalAlloc, StaticHeap}; +use sel4_panicking::catch_unwind; +use sel4_panicking_env::abort; +use sel4_sync::{GenericRawMutex, PanickingMutexSyncOps}; + +use crate::main; + +const STACK_SIZE: usize = 1024 * 64; + +sel4_runtime_common::declare_stack!(STACK_SIZE); + +const HEAP_SIZE: usize = 1024 * 64; + +static STATIC_HEAP: StaticHeap = StaticHeap::new(); + +#[global_allocator] +static GLOBAL_ALLOCATOR: StaticDlmallocGlobalAlloc< + GenericRawMutex, + &'static StaticHeap, +> = StaticDlmallocGlobalAlloc::new( + GenericRawMutex::new(PanickingMutexSyncOps::new()), + &STATIC_HEAP, +); + +sel4_panicking_env::register_debug_put_char!(sel4::debug_put_char); + +#[no_mangle] +unsafe extern "C" fn sel4_runtime_rust_entry() -> ! { + unsafe extern "C" fn cont_fn(_cont_arg: *mut sel4_runtime_common::ContArg) -> ! { + inner_entry() + } + + sel4_runtime_common::initialize_tls_on_stack_and_continue(cont_fn, ptr::null_mut()) +} + +fn inner_entry() -> ! { + unsafe { + sel4_runtime_common::set_eh_frame_finder().unwrap(); + sel4::set_ipc_buffer(get_ipc_buffer().as_mut().unwrap()); + sel4_runtime_common::run_ctors(); + } + + match catch_unwind(main) { + Ok(never) => never, + Err(_) => abort!("main() panicked"), + } +} + +fn get_ipc_buffer() -> *mut sel4::IpcBuffer { + extern "C" { + static _end: usize; + } + unsafe { + (ptr::addr_of!(_end) as usize) + .next_multiple_of(sel4::cap_type::Granule::FRAME_OBJECT_TYPE.bytes()) + as *mut sel4::IpcBuffer + } +} diff --git a/crates/examples/root-task/spawn-task/src/child_vspace.rs b/crates/examples/root-task/spawn-task/src/child_vspace.rs new file mode 100644 index 000000000..bad7ad2e6 --- /dev/null +++ b/crates/examples/root-task/spawn-task/src/child_vspace.rs @@ -0,0 +1,188 @@ +// +// Copyright 2024, Colias Group, LLC +// +// SPDX-License-Identifier: BSD-2-Clause +// + +use alloc::vec::Vec; +use core::ops::Range; + +use object::{ + elf::{PF_R, PF_W, PF_X}, + Object, ObjectSegment, SegmentFlags, +}; + +use crate::{coarsen_footprint, ObjectAllocator}; + +const GRANULE_SIZE: usize = sel4::FrameObjectType::GRANULE.bytes(); + +pub(crate) fn create_child_vspace<'a>( + allocator: &mut ObjectAllocator, + image: &'a impl Object<'a, 'a>, + caller_vspace: sel4::VSpace, + free_page_addr: usize, + asid_pool: sel4::AsidPool, +) -> (sel4::VSpace, usize, sel4::Granule) { + let child_vspace = allocator.allocate_fixed_sized::(); + asid_pool.asid_pool_assign(child_vspace).unwrap(); + + let image_footprint = footprint(image); + + map_intermediate_translation_tables( + allocator, + child_vspace, + image_footprint.start..(image_footprint.end + GRANULE_SIZE), + ); + + map_image( + allocator, + child_vspace, + image_footprint.clone(), + image, + caller_vspace, + free_page_addr, + ); + + let ipc_buffer_addr = image_footprint.end; + let ipc_buffer_cap = allocator.allocate_fixed_sized::(); + ipc_buffer_cap + .frame_map( + child_vspace, + ipc_buffer_addr, + sel4::CapRights::read_write(), + sel4::VmAttributes::default(), + ) + .unwrap(); + + (child_vspace, ipc_buffer_addr, ipc_buffer_cap) +} + +fn footprint<'a>(image: &'a impl Object<'a, 'a>) -> Range { + let min: usize = image + .segments() + .map(|seg| seg.address()) + .min() + .unwrap() + .try_into() + .unwrap(); + let max: usize = image + .segments() + .map(|seg| seg.address() + seg.size()) + .max() + .unwrap() + .try_into() + .unwrap(); + coarsen_footprint(&(min..max), GRANULE_SIZE) +} + +fn map_intermediate_translation_tables( + allocator: &mut ObjectAllocator, + vspace: sel4::VSpace, + footprint: Range, +) { + for level in 1..sel4::vspace_levels::NUM_LEVELS { + let span_bytes = 1 << sel4::vspace_levels::span_bits(level); + let footprint_at_level = coarsen_footprint(&footprint, span_bytes); + for i in 0..(footprint_at_level.len() / span_bytes) { + let ty = sel4::TranslationTableObjectType::from_level(level).unwrap(); + let addr = footprint_at_level.start + i * span_bytes; + allocator + .allocate(ty.blueprint()) + .cast::() + .generic_intermediate_translation_table_map( + ty, + vspace, + addr, + sel4::VmAttributes::default(), + ) + .unwrap() + } + } +} + +fn map_image<'a>( + allocator: &mut ObjectAllocator, + vspace: sel4::VSpace, + footprint: Range, + image: &'a impl Object<'a, 'a>, + caller_vspace: sel4::VSpace, + free_page_addr: usize, +) { + let num_pages = footprint.len() / GRANULE_SIZE; + let mut pages = (0..num_pages) + .map(|_| { + ( + allocator.allocate_fixed_sized::(), + sel4::CapRightsBuilder::none(), + ) + }) + .collect::>(); + + for seg in image.segments() { + let segment_addr = usize::try_from(seg.address()).unwrap(); + let segment_size = usize::try_from(seg.size()).unwrap(); + let segment_footprint = + coarsen_footprint(&(segment_addr..(segment_addr + segment_size)), GRANULE_SIZE); + let num_pages_spanned_by_segment = segment_footprint.len() / GRANULE_SIZE; + let segment_data_size = seg.data().unwrap().len(); + let segment_data_footprint = coarsen_footprint( + &(segment_addr..(segment_addr + segment_data_size)), + GRANULE_SIZE, + ); + let num_pages_spanned_by_segment_data = segment_data_footprint.len() / GRANULE_SIZE; + + let segment_page_index_offset = (segment_footprint.start - footprint.start) / GRANULE_SIZE; + + for (_, rights) in &mut pages[segment_page_index_offset..][..num_pages_spanned_by_segment] { + add_rights(rights, seg.flags()); + } + + let mut data = seg.data().unwrap(); + let mut offset_into_page = segment_addr % GRANULE_SIZE; + for (page_cap, _) in + &pages[segment_page_index_offset..][..num_pages_spanned_by_segment_data] + { + let data_len = (GRANULE_SIZE - offset_into_page).min(data.len()); + + page_cap + .frame_map( + caller_vspace, + free_page_addr, + sel4::CapRights::read_write(), + sel4::VmAttributes::default(), + ) + .unwrap(); + unsafe { + ((free_page_addr + offset_into_page) as *mut u8).copy_from(data.as_ptr(), data_len); + } + page_cap.frame_unmap().unwrap(); + + data = &data[data_len..]; + offset_into_page = 0; + } + } + + for (i, (page_cap, rights)) in pages.into_iter().enumerate() { + let addr = footprint.start + i * GRANULE_SIZE; + page_cap + .frame_map(vspace, addr, rights.build(), sel4::VmAttributes::default()) + .unwrap(); + } +} + +fn add_rights(rights: &mut sel4::CapRightsBuilder, flags: SegmentFlags) { + match flags { + SegmentFlags::Elf { p_flags } => { + if p_flags & PF_R != 0 { + *rights = rights.read(true); + } + if p_flags & PF_W != 0 { + *rights = rights.write(true); + } + if p_flags & PF_X != 0 { + *rights = rights.grant(true); + } + } + _ => unimplemented!(), + } +} diff --git a/crates/examples/root-task/spawn-task/src/main.rs b/crates/examples/root-task/spawn-task/src/main.rs new file mode 100644 index 000000000..589c6b9b5 --- /dev/null +++ b/crates/examples/root-task/spawn-task/src/main.rs @@ -0,0 +1,143 @@ +// +// Copyright 2024, Colias Group, LLC +// +// SPDX-License-Identifier: BSD-2-Clause +// + +#![no_std] +#![no_main] + +extern crate alloc; + +use core::ops::Range; +use core::ptr; + +use object::{File, Object}; + +use sel4_root_task::{root_task, Never}; + +mod child_vspace; +mod object_allocator; + +use child_vspace::create_child_vspace; +use object_allocator::ObjectAllocator; + +const CHILD_ELF_CONTENTS: &[u8] = include_bytes!(env!("CHILD_ELF")); + +#[root_task(heap_size = 1024 * 64)] +fn main(bootinfo: &sel4::BootInfoPtr) -> sel4::Result { + sel4::debug_println!("In root task"); + + let mut object_allocator = ObjectAllocator::new(bootinfo); + + let free_page_addr = init_free_page_addr(bootinfo); + + let child_image = File::parse(CHILD_ELF_CONTENTS).unwrap(); + + let (child_vspace, ipc_buffer_addr, ipc_buffer_cap) = create_child_vspace( + &mut object_allocator, + &child_image, + sel4::init_thread::slot::VSPACE.cap(), + free_page_addr, + sel4::init_thread::slot::ASID_POOL.cap(), + ); + + let inter_task_nfn = object_allocator.allocate_fixed_sized::(); + + let child_cnode_size_bits = 2; + let child_cnode = + object_allocator.allocate_variable_sized::(child_cnode_size_bits); + + child_cnode + .relative_bits_with_depth(1, child_cnode_size_bits) + .mint( + &sel4::init_thread::slot::CNODE + .cap() + .relative(inter_task_nfn), + sel4::CapRights::write_only(), + 0, + ) + .unwrap(); + + let child_tcb = object_allocator.allocate_fixed_sized::(); + + child_tcb + .tcb_configure( + sel4::init_thread::slot::NULL.cptr(), + child_cnode, + sel4::CNodeCapData::new(0, sel4::WORD_SIZE - child_cnode_size_bits), + child_vspace, + ipc_buffer_addr as sel4::Word, + ipc_buffer_cap, + ) + .unwrap(); + + child_cnode + .relative_bits_with_depth(2, child_cnode_size_bits) + .mint( + &sel4::init_thread::slot::CNODE.cap().relative(child_tcb), + sel4::CapRights::all(), + 0, + ) + .unwrap(); + + let mut ctx = sel4::UserContext::default(); + *ctx.pc_mut() = child_image.entry().try_into().unwrap(); + child_tcb.tcb_write_all_registers(true, &mut ctx).unwrap(); + + inter_task_nfn.wait(); + + sel4::debug_println!("TEST_PASS"); + + sel4::init_thread::suspend_self() +} + +// // // + +#[repr(C, align(4096))] +struct FreePagePlaceHolder(#[allow(dead_code)] [u8; sel4::FrameObjectType::GRANULE.bytes()]); + +static FREE_PAGE_PLACEHOLDER: FreePagePlaceHolder = + FreePagePlaceHolder([0; sel4::FrameObjectType::GRANULE.bytes()]); + +fn init_free_page_addr(bootinfo: &sel4::BootInfo) -> usize { + let addr = ptr::addr_of!(FREE_PAGE_PLACEHOLDER) as usize; + get_user_image_frame_slot(bootinfo, addr) + .cap() + .frame_unmap() + .unwrap(); + addr +} + +fn get_user_image_frame_slot( + bootinfo: &sel4::BootInfo, + addr: usize, +) -> sel4::init_thread::Slot { + assert_eq!(addr % sel4::FrameObjectType::GRANULE.bytes(), 0); + let user_image_footprint = get_user_image_footprint(); + let num_user_frames = bootinfo.user_image_frames().len(); + assert_eq!( + user_image_footprint.len(), + num_user_frames * sel4::FrameObjectType::GRANULE.bytes() + ); + let ix = (addr - user_image_footprint.start) / sel4::FrameObjectType::GRANULE.bytes(); + bootinfo.user_image_frames().index(ix) +} + +fn get_user_image_footprint() -> Range { + extern "C" { + static __executable_start: usize; + static _end: usize; + } + let precise_footprint = + unsafe { (ptr::addr_of!(__executable_start) as usize)..(ptr::addr_of!(_end) as usize) }; + coarsen_footprint(&precise_footprint, sel4::FrameObjectType::GRANULE.bytes()) +} + +fn coarsen_footprint(footprint: &Range, granularity: usize) -> Range { + round_down(footprint.start, granularity)..footprint.end.next_multiple_of(granularity) +} + +const fn round_down(n: usize, b: usize) -> usize { + n - n % b +} diff --git a/crates/examples/root-task/spawn-task/src/object_allocator.rs b/crates/examples/root-task/spawn-task/src/object_allocator.rs new file mode 100644 index 000000000..34a83b630 --- /dev/null +++ b/crates/examples/root-task/spawn-task/src/object_allocator.rs @@ -0,0 +1,59 @@ +// +// Copyright 2024, Colias Group, LLC +// +// SPDX-License-Identifier: BSD-2-Clause +// + +use core::ops::Range; + +pub(crate) struct ObjectAllocator { + empty_slots: Range, + ut: sel4::Untyped, +} + +impl ObjectAllocator { + pub(crate) fn new(bootinfo: &sel4::BootInfo) -> Self { + Self { + empty_slots: bootinfo.empty().range(), + ut: find_largest_untyped(bootinfo), + } + } + + pub(crate) fn allocate(&mut self, blueprint: sel4::ObjectBlueprint) -> sel4::Unspecified { + let slot_index = self.empty_slots.next().unwrap(); + self.ut + .untyped_retype( + &blueprint, + &sel4::init_thread::slot::CNODE.cap().relative_self(), + slot_index, + 1, + ) + .unwrap(); + sel4::init_thread::Slot::from_index(slot_index).cap() + } + + pub(crate) fn allocate_fixed_sized( + &mut self, + ) -> sel4::Cap { + self.allocate(T::object_blueprint()).cast() + } + + pub(crate) fn allocate_variable_sized( + &mut self, + size_bits: usize, + ) -> sel4::Cap { + self.allocate(T::object_blueprint(size_bits)).cast() + } +} + +fn find_largest_untyped(bootinfo: &sel4::BootInfo) -> sel4::Untyped { + let (ut_ix, _desc) = bootinfo + .untyped_list() + .iter() + .enumerate() + .filter(|(_i, desc)| !desc.is_device()) + .max_by_key(|(_i, desc)| desc.size_bits()) + .unwrap(); + + bootinfo.untyped().index(ut_ix).cap() +} diff --git a/hacking/nix/scope/world/instances/default.nix b/hacking/nix/scope/world/instances/default.nix index 8e9e1df40..e2a3de84a 100644 --- a/hacking/nix/scope/world/instances/default.nix +++ b/hacking/nix/scope/world/instances/default.nix @@ -353,6 +353,32 @@ in rec { canAutomateSimply = true; }; }); + + spawn-task = maybe haveFullRuntime ( + let + child = mkTask { + rootCrate = crates.spawn-task-child; + release = false; + }; + in + mkInstance { + rootTask = mkTask { + rootCrate = crates.spawn-task; + release = false; + lastLayerModifications.modifyDerivation = drv: drv.overrideAttrs (attrs: { + # CHILD_ELF = writeText "x" "foo"; + # CHILD_ELF = child.split.min; + CHILD_ELF = child.split.full; + passthru = (attrs.passthru or {}) // { + inherit child; + }; + }); + }; + extraPlatformArgs = lib.optionalAttrs canSimulate { + canAutomateSimply = true; + }; + } + ); }; }; } diff --git a/hacking/nix/scope/world/shell.nix b/hacking/nix/scope/world/shell.nix index b9abab788..7df6a671d 100644 --- a/hacking/nix/scope/world/shell.nix +++ b/hacking/nix/scope/world/shell.nix @@ -47,8 +47,13 @@ let name = "BINDGEN_EXTRA_CLANG_ARGS_${target.name}"; value = [ "-I${libcDir}/include" ]; })); + + miscEnvVars = { + CHILD_ELF = emptyFile; + }; + in -mkShell (seL4RustEnvVars // kernelLoaderConfigEnvVars // capdlEnvVars // bindgenEnvVars // { +mkShell (seL4RustEnvVars // kernelLoaderConfigEnvVars // capdlEnvVars // bindgenEnvVars // miscEnvVars // { # TODO RUST_SEL4_TARGET = defaultRustTargetInfo.name;