From 466c8de6eb13a6071d35374ff0cbd25b89fef7c9 Mon Sep 17 00:00:00 2001 From: Nick Spinale Date: Wed, 20 Mar 2024 05:37:40 +0000 Subject: [PATCH] Add serial-device example Signed-off-by: Nick Spinale --- Cargo.lock | 9 + Cargo.toml | 1 + .../root-task/serial-device/Cargo.nix | 18 ++ .../root-task/serial-device/Cargo.toml | 22 ++ .../root-task/serial-device/src/device.rs | 87 +++++++ .../root-task/serial-device/src/main.rs | 213 ++++++++++++++++++ hacking/nix/scope/world/instances/default.nix | 11 + 7 files changed, 361 insertions(+) create mode 100644 crates/examples/root-task/serial-device/Cargo.nix create mode 100644 crates/examples/root-task/serial-device/Cargo.toml create mode 100644 crates/examples/root-task/serial-device/src/device.rs create mode 100644 crates/examples/root-task/serial-device/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 7c8ae18b3..82c799c98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2702,6 +2702,15 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "serial-device" +version = "0.1.0" +dependencies = [ + "sel4", + "sel4-root-task", + "tock-registers", +] + [[package]] name = "sha2" version = "0.10.8" diff --git a/Cargo.toml b/Cargo.toml index c13d696b4..ccda8e64b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ 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/serial-device", "crates/examples/root-task/spawn-task", "crates/examples/root-task/spawn-task/child", "crates/examples/root-task/spawn-thread", diff --git a/crates/examples/root-task/serial-device/Cargo.nix b/crates/examples/root-task/serial-device/Cargo.nix new file mode 100644 index 000000000..1d9e24795 --- /dev/null +++ b/crates/examples/root-task/serial-device/Cargo.nix @@ -0,0 +1,18 @@ +# +# Copyright 2024, Colias Group, LLC +# +# SPDX-License-Identifier: BSD-2-Clause +# + +{ mk, versions, localCrates }: + +mk { + package.name = "serial-device"; + dependencies = { + inherit (versions) tock-registers; + inherit (localCrates) + sel4 + sel4-root-task + ; + }; +} diff --git a/crates/examples/root-task/serial-device/Cargo.toml b/crates/examples/root-task/serial-device/Cargo.toml new file mode 100644 index 000000000..e3b57c79b --- /dev/null +++ b/crates/examples/root-task/serial-device/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 = "serial-device" +version = "0.1.0" +authors = ["Nick Spinale "] +edition = "2021" +license = "BSD-2-Clause" + +[dependencies] +sel4 = { path = "../../../sel4" } +sel4-root-task = { path = "../../../sel4-root-task" } +tock-registers = "0.8.1" diff --git a/crates/examples/root-task/serial-device/src/device.rs b/crates/examples/root-task/serial-device/src/device.rs new file mode 100644 index 000000000..ce6f80e52 --- /dev/null +++ b/crates/examples/root-task/serial-device/src/device.rs @@ -0,0 +1,87 @@ +// +// Copyright 2024, Colias Group, LLC +// +// SPDX-License-Identifier: BSD-2-Clause +// + +use core::ops::Deref; + +use tock_registers::interfaces::{Readable, Writeable}; +use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly}; +use tock_registers::{register_bitfields, register_structs}; + +register_structs! { + #[allow(non_snake_case)] + pub RegisterBlock { + (0x000 => DR: ReadWrite), + (0x001 => _reserved0), + (0x018 => FR: ReadOnly), + (0x01c => _reserved1), + (0x038 => IMSC: ReadWrite), + (0x03c => _reserved2), + (0x044 => ICR: WriteOnly), + (0x048 => @END), + } +} + +register_bitfields! { + u32, + + FR [ + TXFF OFFSET(5) NUMBITS(1) [], + RXFE OFFSET(4) NUMBITS(1) [], + ], + + IMSC [ + RXIM OFFSET(4) NUMBITS(1) [], + ], + + ICR [ + ALL OFFSET(0) NUMBITS(11) [], + ], +} + +pub struct Device { + ptr: *mut RegisterBlock, +} + +impl Device { + pub unsafe fn new(ptr: *mut RegisterBlock) -> Self { + Self { ptr } + } + + fn ptr(&self) -> *mut RegisterBlock { + self.ptr + } + + pub fn init(&self) { + self.IMSC.write(IMSC::RXIM::SET); + } + + pub fn put_char(&self, c: u8) { + while self.FR.matches_all(FR::TXFF::SET) { + core::hint::spin_loop(); + } + self.DR.set(c) + } + + pub fn get_char(&self) -> Option { + if self.FR.matches_all(FR::RXFE::CLEAR) { + Some(self.DR.get()) + } else { + None + } + } + + pub fn clear_all_interrupts(&self) { + self.ICR.write(ICR::ALL::SET); + } +} + +impl Deref for Device { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} diff --git a/crates/examples/root-task/serial-device/src/main.rs b/crates/examples/root-task/serial-device/src/main.rs new file mode 100644 index 000000000..cbdba1f3e --- /dev/null +++ b/crates/examples/root-task/serial-device/src/main.rs @@ -0,0 +1,213 @@ +// +// Copyright 2024, Colias Group, LLC +// +// SPDX-License-Identifier: BSD-2-Clause +// + +#![no_std] +#![no_main] + +use core::ptr; + +use sel4::CapTypeForObjectOfFixedSize; +use sel4_root_task::{root_task, Never}; + +mod device; + +use device::{Device, RegisterBlock}; + +const SERIAL_DEVICE_IRQ: usize = 33; + +const SERIAL_DEVICE_BASE_ADDR: usize = 0x0900_0000; + +#[root_task] +fn main(bootinfo: &sel4::BootInfoPtr) -> sel4::Result { + let mut empty_slots = bootinfo + .empty() + .range() + .map(sel4::init_thread::Slot::from_index); + + let kernel_ut = find_largest_kernel_untyped(bootinfo); + + let irq_handler_cap = empty_slots + .next() + .unwrap() + .downcast::() + .cap(); + + sel4::init_thread::slot::IRQ_CONTROL + .cap() + .irq_control_get( + SERIAL_DEVICE_IRQ.try_into().unwrap(), + &sel4::init_thread::slot::CNODE + .cap() + .relative(irq_handler_cap), + ) + .unwrap(); + + let irq_notification_slot = empty_slots + .next() + .unwrap() + .downcast::(); + + kernel_ut + .untyped_retype( + &sel4::ObjectBlueprint::Notification, + &sel4::init_thread::slot::CNODE.cap().relative_self(), + irq_notification_slot.index(), + 1, + ) + .unwrap(); + + let irq_notification_cap = irq_notification_slot.cap(); + + irq_handler_cap + .irq_handler_set_notification(irq_notification_cap) + .unwrap(); + + let (device_ut_ix, device_ut_desc) = bootinfo + .untyped_list() + .iter() + .enumerate() + .find(|(_i, desc)| { + (desc.paddr()..(desc.paddr() + (1 << desc.size_bits()))) + .contains(&SERIAL_DEVICE_BASE_ADDR) + }) + .unwrap(); + + assert!(device_ut_desc.is_device()); + + let device_ut_cap = bootinfo.untyped().index(device_ut_ix).cap(); + + trim_untyped( + device_ut_cap, + device_ut_desc.paddr(), + SERIAL_DEVICE_BASE_ADDR, + empty_slots.next().unwrap(), + empty_slots.next().unwrap(), + ); + + let serial_device_frame_slot = empty_slots + .next() + .unwrap() + .downcast::(); + + device_ut_cap + .untyped_retype( + &sel4::cap_type::Granule::object_blueprint(), + &sel4::init_thread::slot::CNODE.cap().relative_self(), + serial_device_frame_slot.index(), + 1, + ) + .unwrap(); + + let serial_device_frame_cap = serial_device_frame_slot.cap(); + + let serial_device_frame_addr = init_free_page_addr(bootinfo); + + serial_device_frame_cap + .frame_map( + sel4::init_thread::slot::VSPACE.cap(), + serial_device_frame_addr, + sel4::CapRights::read_write(), + sel4::VmAttributes::default(), + ) + .unwrap(); + + let serial_device = unsafe { Device::new(serial_device_frame_addr as *mut RegisterBlock) }; + + serial_device.init(); + + for c in b"echo> ".iter() { + serial_device.put_char(*c); + } + + loop { + serial_device.clear_all_interrupts(); + irq_handler_cap.irq_handler_ack().unwrap(); + + irq_notification_cap.wait(); + + while let Some(c) = serial_device.get_char() { + serial_device.put_char(b'['); + serial_device.put_char(c); + serial_device.put_char(b']'); + } + } +} + +// // // + +fn find_largest_kernel_untyped(bootinfo: &sel4::BootInfo) -> sel4::cap::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() +} + +// // // + +fn trim_untyped( + ut: sel4::cap::Untyped, + ut_paddr: usize, + target_paddr: usize, + free_slot_a: sel4::init_thread::Slot, + free_slot_b: sel4::init_thread::Slot, +) { + let rel_a = sel4::init_thread::slot::CNODE + .cap() + .relative(free_slot_a.cptr()); + let rel_b = sel4::init_thread::slot::CNODE + .cap() + .relative(free_slot_b.cptr()); + let mut cur_paddr = ut_paddr; + while cur_paddr != target_paddr { + let size_bits = (target_paddr - cur_paddr).ilog2().try_into().unwrap(); + ut.untyped_retype( + &sel4::ObjectBlueprint::Untyped { size_bits }, + &sel4::init_thread::slot::CNODE.cap().relative_self(), + free_slot_b.index(), + 1, + ) + .unwrap(); + rel_a.delete().unwrap(); + rel_a.move_(&rel_b).unwrap(); + cur_paddr += 1 << size_bits; + } +} + +// // // + +#[repr(C, align(4096))] +struct FreePagePlaceHolder(#[allow(dead_code)] [u8; GRANULE_SIZE]); + +static mut FREE_PAGE_PLACEHOLDER: FreePagePlaceHolder = FreePagePlaceHolder([0; GRANULE_SIZE]); + +fn init_free_page_addr(bootinfo: &sel4::BootInfo) -> usize { + let addr = unsafe { 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 { + extern "C" { + static __executable_start: usize; + } + let user_image_addr = unsafe { ptr::addr_of!(__executable_start) as usize }; + bootinfo + .user_image_frames() + .index(addr / GRANULE_SIZE - user_image_addr / GRANULE_SIZE) +} + +const GRANULE_SIZE: usize = sel4::FrameObjectType::GRANULE.bytes(); diff --git a/hacking/nix/scope/world/instances/default.nix b/hacking/nix/scope/world/instances/default.nix index 66ad0bac8..0e5674cf5 100644 --- a/hacking/nix/scope/world/instances/default.nix +++ b/hacking/nix/scope/world/instances/default.nix @@ -92,6 +92,7 @@ in rec { examples.root-task.example-root-task-without-runtime examples.root-task.spawn-thread examples.root-task.spawn-task + examples.root-task.serial-device ]; allAutomationScripts = map @@ -345,6 +346,16 @@ in rec { }; }); + serial-device = maybe (haveFullRuntime && hostPlatform.isAarch) (mkInstance { + rootTask = mkTask { + rootCrate = crates.serial-device; + release = false; + }; + extraPlatformArgs = lib.optionalAttrs canSimulate { + canAutomateSimply = true; + }; + }); + spawn-thread = maybe haveFullRuntime (mkInstance { rootTask = mkTask { rootCrate = crates.spawn-thread;